[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 <alleteam@gmail.com>
This commit is contained in:
Nikolay Minaylov 2022-01-21 19:55:44 +03:00 committed by GitHub
parent d4d87aa6a8
commit d4787e859e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 968 additions and 182 deletions

View File

@ -71,7 +71,7 @@ void bad_usb_app_free(BadUsbApp* app) {
} }
int32_t bad_usb_app(void* p) { 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); furi_hal_usb_set_config(&usb_hid);
BadUsbApp* bad_usb_app = bad_usb_app_alloc(); BadUsbApp* bad_usb_app = bad_usb_app_alloc();

View File

@ -41,7 +41,7 @@ int32_t usb_mouse_app(void* p) {
furi_check(event_queue); furi_check(event_queue);
ViewPort* view_port = view_port_alloc(); 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); furi_hal_usb_set_config(&usb_hid);
view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL); view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL);

View File

@ -15,6 +15,7 @@ typedef struct {
typedef enum { typedef enum {
UsbTestSubmenuIndexEnable, UsbTestSubmenuIndexEnable,
UsbTestSubmenuIndexDisable, UsbTestSubmenuIndexDisable,
UsbTestSubmenuIndexRestart,
UsbTestSubmenuIndexVcpSingle, UsbTestSubmenuIndexVcpSingle,
UsbTestSubmenuIndexVcpDual, UsbTestSubmenuIndexVcpDual,
UsbTestSubmenuIndexHid, UsbTestSubmenuIndexHid,
@ -28,6 +29,8 @@ void usb_test_submenu_callback(void* context, uint32_t index) {
furi_hal_usb_enable(); furi_hal_usb_enable();
} else if(index == UsbTestSubmenuIndexDisable) { } else if(index == UsbTestSubmenuIndexDisable) {
furi_hal_usb_disable(); furi_hal_usb_disable();
} else if(index == UsbTestSubmenuIndexRestart) {
furi_hal_usb_reinit();
} else if(index == UsbTestSubmenuIndexVcpSingle) { } else if(index == UsbTestSubmenuIndexVcpSingle) {
furi_hal_usb_set_config(&usb_cdc_single); furi_hal_usb_set_config(&usb_cdc_single);
} else if(index == UsbTestSubmenuIndexVcpDual) { } else if(index == UsbTestSubmenuIndexVcpDual) {
@ -60,6 +63,8 @@ UsbTestApp* usb_test_app_alloc() {
app->submenu, "Enable", UsbTestSubmenuIndexEnable, usb_test_submenu_callback, app); app->submenu, "Enable", UsbTestSubmenuIndexEnable, usb_test_submenu_callback, app);
submenu_add_item( submenu_add_item(
app->submenu, "Disable", UsbTestSubmenuIndexDisable, usb_test_submenu_callback, app); app->submenu, "Disable", UsbTestSubmenuIndexDisable, usb_test_submenu_callback, app);
submenu_add_item(
app->submenu, "Restart", UsbTestSubmenuIndexRestart, usb_test_submenu_callback, app);
submenu_add_item( submenu_add_item(
app->submenu, "Single VCP", UsbTestSubmenuIndexVcpSingle, usb_test_submenu_callback, app); app->submenu, "Single VCP", UsbTestSubmenuIndexVcpSingle, usb_test_submenu_callback, app);
submenu_add_item( submenu_add_item(

View File

@ -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_context(usb_uart->tx_thread, usb_uart);
furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); 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_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);

View File

@ -77,7 +77,7 @@ static bool file_select_input_callback(InputEvent* event, void* context) {
FileSelect* file_select = (FileSelect*)context; FileSelect* file_select = (FileSelect*)context;
bool consumed = false; bool consumed = false;
if(event->type == InputTypeShort) { if((event->type == InputTypeShort) | (event->type == InputTypeRepeat)) {
if(!file_select->init_completed) { if(!file_select->init_completed) {
if(!file_select_init_inner(file_select)) { if(!file_select_init_inner(file_select)) {
file_select->callback(false, file_select->context); file_select->callback(false, file_select->context);

View File

@ -87,6 +87,14 @@ static bool menu_input_callback(InputEvent* event, void* context) {
consumed = true; consumed = true;
menu_process_ok(menu); 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; return consumed;

View File

@ -106,6 +106,14 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) {
default: default:
break; 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; return consumed;

View File

@ -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, 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)) { if(item->current_value_index < (item->values_count - 1)) {
canvas_draw_str(canvas, 115, item_text_y, ">"); canvas_draw_str(canvas, 115, item_text_y, ">");
@ -147,6 +153,27 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context)
default: default:
break; 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; return consumed;

View File

@ -2,6 +2,11 @@
#include <furi_hal.h> #include <furi_hal.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
typedef struct {
osThreadId_t thread;
} PowerObserverSrv;
const NotificationMessage message_green_110 = { const NotificationMessage message_green_110 = {
.type = NotificationMessageTypeLedGreen, .type = NotificationMessageTypeLedGreen,
.data.led.value = 110, .data.led.value = 110,
@ -14,20 +19,58 @@ static const NotificationSequence sequence_overconsumption = {
NULL, 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) { int32_t power_observer_srv(void* p) {
NotificationApp* notifications = furi_record_open("notification"); NotificationApp* notifications = furi_record_open("notification");
PowerObserverSrv* srv = furi_alloc(sizeof(PowerObserverSrv));
srv->thread = osThreadGetId();
const float overconsumption_limit = 0.03f; 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) { while(true) {
float current = -furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); uint32_t flags = osThreadFlagsWait(EventReset | EventRequest, osFlagsWaitAny, 500);
if((flags & osFlagsError) == 0) {
if(current >= overconsumption_limit) { if(flags & EventReset) {
notification_message_block(notifications, &sequence_overconsumption); 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; return 0;
} }

View File

@ -190,7 +190,7 @@ static int32_t u2f_hid_worker(void* context) {
FURI_LOG_D(WORKER_TAG, "Init"); 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); furi_hal_usb_set_config(&usb_hid_u2f);
u2f_hid->lock_timer = osTimerNew(u2f_hid_lock_timeout_callback, osTimerOnce, u2f_hid, NULL); u2f_hid->lock_timer = osTimerNew(u2f_hid_lock_timeout_callback, osTimerOnce, u2f_hid, NULL);

View File

@ -134,3 +134,72 @@ bool furi_hal_i2c_trx(
return false; 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, &reg_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, &reg_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);
}

View File

@ -84,6 +84,112 @@ bool furi_hal_i2c_trx(
const uint8_t rx_size, const uint8_t rx_size,
uint32_t timeout); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -134,3 +134,72 @@ bool furi_hal_i2c_trx(
return false; 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, &reg_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, &reg_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);
}

View File

@ -84,6 +84,112 @@ bool furi_hal_i2c_trx(
const uint8_t rx_size, const uint8_t rx_size,
uint32_t timeout); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -28,6 +28,7 @@ void furi_hal_light_init() {
} }
void furi_hal_light_set(Light light, uint8_t value) { void furi_hal_light_set(Light light, uint8_t value) {
uint8_t prev = 0;
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
switch(light) { switch(light) {
case LightRed: 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); lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value);
break; break;
case LightBacklight: 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; break;
default: default:
break; break;
} }
furi_hal_i2c_release(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power);
} }

View File

@ -216,6 +216,13 @@ bool furi_hal_power_is_otg_enabled() {
return ret; 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() { uint32_t furi_hal_power_get_battery_remaining_capacity() {
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
uint32_t ret = bq27220_get_remaining_capacity(&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) { BQ27220_ERROR) {
printf("Failed to get bq27220 status. Communication error.\r\n"); printf("Failed to get bq27220 status. Communication error.\r\n");
} else { } else {
// Operation status register
printf( printf(
"bq27220: CALMD: %d, SEC0: %d, SEC1: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n", "bq27220: CALMD: %d, SEC0: %d, SEC1: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n",
operation_status.CALMD, operation_status.CALMD,

View File

@ -9,15 +9,19 @@
#define USB_RECONNECT_DELAY 500 #define USB_RECONNECT_DELAY 500
static UsbInterface* usb_if_cur; static FuriHalUsbInterface* usb_if_cur;
static UsbInterface* usb_if_next; static FuriHalUsbInterface* usb_if_next;
static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
static uint32_t ubuf[0x20]; static uint32_t ubuf[0x20];
usbd_device udev; 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 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 susp_evt(usbd_device* dev, uint8_t event, uint8_t ep);
static void wkup_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; osTimerId_t reconnect_tmr;
bool enabled; bool enabled;
bool connected; bool connected;
bool mode_changing;
} usb_config; } usb_config;
static void furi_hal_usb_tmr_cb(void* context); 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_descr(&udev, usb_descriptor_get);
usbd_reg_event(&udev, usbd_evt_susp, susp_evt); usbd_reg_event(&udev, usbd_evt_susp, susp_evt);
usbd_reg_event(&udev, usbd_evt_wkup, wkup_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.enabled = false;
usb_config.reconnect_tmr = NULL; usb_config.reconnect_tmr = NULL;
@ -57,28 +63,49 @@ void furi_hal_usb_init(void) {
FURI_LOG_I(TAG, "Init OK"); FURI_LOG_I(TAG, "Init OK");
} }
void furi_hal_usb_set_config(UsbInterface* new_if) { void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) {
if(new_if != usb_if_cur) { if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage
if(usb_config.enabled) { usb_config.mode_changing = true;
usb_if_next = new_if; usb_if_next = new_if;
if(usb_config.reconnect_tmr == NULL) if(usb_config.reconnect_tmr == NULL)
usb_config.reconnect_tmr = usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); furi_hal_usb_disable();
furi_hal_usb_disable(); usb_config.mode_changing = true;
osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
} else { } else if(
if(usb_if_cur != NULL) usb_if_cur->deinit(&udev); (usb_config.mode_changing) &&
if(new_if != NULL) { (usb_if_next != new_if)) { // Last interface mode change wasn't completed
new_if->init(&udev, new_if); osTimerStop(usb_config.reconnect_tmr);
FURI_LOG_I(TAG, "USB mode change"); usb_if_next = new_if;
usb_config.enabled = true; osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
usb_if_cur = new_if; } 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; return usb_if_cur;
} }
@ -113,6 +140,9 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
switch(dtype) { switch(dtype) {
case USB_DTYPE_DEVICE: case USB_DTYPE_DEVICE:
if(callback != NULL) {
callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx);
}
desc = usb_if_cur->dev_descr; desc = usb_if_cur->dev_descr;
break; break;
case USB_DTYPE_CONFIGURATION: case USB_DTYPE_CONFIGURATION:
@ -122,11 +152,11 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
case USB_DTYPE_STRING: case USB_DTYPE_STRING:
if(dnumber == UsbDevLang) { if(dnumber == UsbDevLang) {
desc = &dev_lang_desc; 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; 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; 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; desc = usb_if_cur->str_serial_descr;
} else } else
return usbd_fail; return usbd_fail;
@ -144,11 +174,25 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
return usbd_ack; 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) { static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
if((usb_if_cur != NULL) && (usb_config.connected == true)) { if((usb_if_cur != NULL) && (usb_config.connected == true)) {
usb_config.connected = false; usb_config.connected = false;
usb_if_cur->suspend(&udev); 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) { 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_config.connected = true;
usb_if_cur->wakeup(&udev); usb_if_cur->wakeup(&udev);
} }
if(callback != NULL) {
callback(FuriHalUsbStateEventWakeup, cb_ctx);
}
} }

View File

@ -385,7 +385,7 @@ static const struct CdcConfigDescriptorDual
static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {}; static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {};
static uint8_t cdc_ctrl_line_state[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_deinit(usbd_device* dev);
static void cdc_on_wakeup(usbd_device* dev); static void cdc_on_wakeup(usbd_device* dev);
static void cdc_on_suspend(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_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_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
static usbd_device* usb_dev; static usbd_device* usb_dev;
static UsbInterface* cdc_if_cur = NULL; static FuriHalUsbInterface* cdc_if_cur = NULL;
static bool connected = false; static bool connected = false;
static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL}; static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL};
static void* cb_ctx[IF_NUM_MAX]; static void* cb_ctx[IF_NUM_MAX];
UsbInterface usb_cdc_single = { FuriHalUsbInterface usb_cdc_single = {
.init = cdc_init, .init = cdc_init,
.deinit = cdc_deinit, .deinit = cdc_deinit,
.wakeup = cdc_on_wakeup, .wakeup = cdc_on_wakeup,
@ -413,7 +413,7 @@ UsbInterface usb_cdc_single = {
.cfg_descr = (void*)&cdc_cfg_desc_single, .cfg_descr = (void*)&cdc_cfg_desc_single,
}; };
UsbInterface usb_cdc_dual = { FuriHalUsbInterface usb_cdc_dual = {
.init = cdc_init, .init = cdc_init,
.deinit = cdc_deinit, .deinit = cdc_deinit,
.wakeup = cdc_on_wakeup, .wakeup = cdc_on_wakeup,
@ -428,7 +428,7 @@ UsbInterface usb_cdc_dual = {
.cfg_descr = (void*)&cdc_cfg_desc_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; usb_dev = dev;
cdc_if_cur = intf; cdc_if_cur = intf;

View File

@ -236,7 +236,7 @@ static struct HidReport {
struct HidReportConsumer consumer; struct HidReportConsumer consumer;
} __attribute__((packed)) hid_report; } __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_deinit(usbd_device* dev);
static void hid_on_wakeup(usbd_device* dev); static void hid_on_wakeup(usbd_device* dev);
static void hid_on_suspend(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); return hid_send_report(ReportIdConsumer);
} }
UsbInterface usb_hid = { FuriHalUsbInterface usb_hid = {
.init = hid_init, .init = hid_init,
.deinit = hid_deinit, .deinit = hid_deinit,
.wakeup = hid_on_wakeup, .wakeup = hid_on_wakeup,
@ -363,7 +363,7 @@ UsbInterface usb_hid = {
.cfg_descr = (void*)&hid_cfg_desc, .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); if(hid_semaphore == NULL) hid_semaphore = osSemaphoreNew(1, 1, NULL);
usb_dev = dev; usb_dev = dev;
hid_report.keyboard.report_id = ReportIdKeyboard; hid_report.keyboard.report_id = ReportIdKeyboard;

View File

@ -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_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_prod_desc = USB_STRING_DESC("U2F Token");
static const struct usb_string_descriptor dev_serial_desc = USB_STRING_DESC("TODO: serial");
/* Device descriptor */ /* Device descriptor */
static const struct usb_device_descriptor hid_u2f_device_desc = { 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), .bcdDevice = VERSION_BCD(1, 0, 0),
.iManufacturer = UsbDevManuf, .iManufacturer = UsbDevManuf,
.iProduct = UsbDevProduct, .iProduct = UsbDevProduct,
.iSerialNumber = UsbDevSerial, .iSerialNumber = 0,
.bNumConfigurations = 1, .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_deinit(usbd_device* dev);
static void hid_u2f_on_wakeup(usbd_device* dev); static void hid_u2f_on_wakeup(usbd_device* dev);
static void hid_u2f_on_suspend(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, .init = hid_u2f_init,
.deinit = hid_u2f_deinit, .deinit = hid_u2f_deinit,
.wakeup = hid_u2f_on_wakeup, .wakeup = hid_u2f_on_wakeup,
@ -181,12 +180,12 @@ UsbInterface usb_hid_u2f = {
.str_manuf_descr = (void*)&dev_manuf_desc, .str_manuf_descr = (void*)&dev_manuf_desc,
.str_prod_descr = (void*)&dev_prod_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, .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); if(hid_u2f_semaphore == NULL) hid_u2f_semaphore = osSemaphoreNew(1, 1, NULL);
usb_dev = dev; usb_dev = dev;

View File

@ -28,6 +28,7 @@ void furi_hal_light_init() {
} }
void furi_hal_light_set(Light light, uint8_t value) { void furi_hal_light_set(Light light, uint8_t value) {
uint8_t prev = 0;
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
switch(light) { switch(light) {
case LightRed: 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); lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value);
break; break;
case LightBacklight: 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; break;
default: default:
break; break;
} }
furi_hal_i2c_release(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power);
} }

View File

@ -216,6 +216,13 @@ bool furi_hal_power_is_otg_enabled() {
return ret; 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() { uint32_t furi_hal_power_get_battery_remaining_capacity() {
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
uint32_t ret = bq27220_get_remaining_capacity(&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) { BQ27220_ERROR) {
printf("Failed to get bq27220 status. Communication error.\r\n"); printf("Failed to get bq27220 status. Communication error.\r\n");
} else { } else {
// Operation status register
printf( printf(
"bq27220: CALMD: %d, SEC0: %d, SEC1: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n", "bq27220: CALMD: %d, SEC0: %d, SEC1: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n",
operation_status.CALMD, operation_status.CALMD,

View File

@ -9,15 +9,19 @@
#define USB_RECONNECT_DELAY 500 #define USB_RECONNECT_DELAY 500
static UsbInterface* usb_if_cur; static FuriHalUsbInterface* usb_if_cur;
static UsbInterface* usb_if_next; static FuriHalUsbInterface* usb_if_next;
static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
static uint32_t ubuf[0x20]; static uint32_t ubuf[0x20];
usbd_device udev; 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 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 susp_evt(usbd_device* dev, uint8_t event, uint8_t ep);
static void wkup_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; osTimerId_t reconnect_tmr;
bool enabled; bool enabled;
bool connected; bool connected;
bool mode_changing;
} usb_config; } usb_config;
static void furi_hal_usb_tmr_cb(void* context); 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_descr(&udev, usb_descriptor_get);
usbd_reg_event(&udev, usbd_evt_susp, susp_evt); usbd_reg_event(&udev, usbd_evt_susp, susp_evt);
usbd_reg_event(&udev, usbd_evt_wkup, wkup_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.enabled = false;
usb_config.reconnect_tmr = NULL; usb_config.reconnect_tmr = NULL;
@ -57,28 +63,49 @@ void furi_hal_usb_init(void) {
FURI_LOG_I(TAG, "Init OK"); FURI_LOG_I(TAG, "Init OK");
} }
void furi_hal_usb_set_config(UsbInterface* new_if) { void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) {
if(new_if != usb_if_cur) { if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage
if(usb_config.enabled) { usb_config.mode_changing = true;
usb_if_next = new_if; usb_if_next = new_if;
if(usb_config.reconnect_tmr == NULL) if(usb_config.reconnect_tmr == NULL)
usb_config.reconnect_tmr = usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); furi_hal_usb_disable();
furi_hal_usb_disable(); usb_config.mode_changing = true;
osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
} else { } else if(
if(usb_if_cur != NULL) usb_if_cur->deinit(&udev); (usb_config.mode_changing) &&
if(new_if != NULL) { (usb_if_next != new_if)) { // Last interface mode change wasn't completed
new_if->init(&udev, new_if); osTimerStop(usb_config.reconnect_tmr);
FURI_LOG_I(TAG, "USB mode change"); usb_if_next = new_if;
usb_config.enabled = true; osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
usb_if_cur = new_if; } 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; return usb_if_cur;
} }
@ -113,6 +140,9 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
switch(dtype) { switch(dtype) {
case USB_DTYPE_DEVICE: case USB_DTYPE_DEVICE:
if(callback != NULL) {
callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx);
}
desc = usb_if_cur->dev_descr; desc = usb_if_cur->dev_descr;
break; break;
case USB_DTYPE_CONFIGURATION: case USB_DTYPE_CONFIGURATION:
@ -122,11 +152,11 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
case USB_DTYPE_STRING: case USB_DTYPE_STRING:
if(dnumber == UsbDevLang) { if(dnumber == UsbDevLang) {
desc = &dev_lang_desc; 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; 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; 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; desc = usb_if_cur->str_serial_descr;
} else } else
return usbd_fail; return usbd_fail;
@ -144,11 +174,25 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
return usbd_ack; 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) { static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
if((usb_if_cur != NULL) && (usb_config.connected == true)) { if((usb_if_cur != NULL) && (usb_config.connected == true)) {
usb_config.connected = false; usb_config.connected = false;
usb_if_cur->suspend(&udev); 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) { 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_config.connected = true;
usb_if_cur->wakeup(&udev); usb_if_cur->wakeup(&udev);
} }
if(callback != NULL) {
callback(FuriHalUsbStateEventWakeup, cb_ctx);
}
} }

View File

@ -385,7 +385,7 @@ static const struct CdcConfigDescriptorDual
static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {}; static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {};
static uint8_t cdc_ctrl_line_state[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_deinit(usbd_device* dev);
static void cdc_on_wakeup(usbd_device* dev); static void cdc_on_wakeup(usbd_device* dev);
static void cdc_on_suspend(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_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_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
static usbd_device* usb_dev; static usbd_device* usb_dev;
static UsbInterface* cdc_if_cur = NULL; static FuriHalUsbInterface* cdc_if_cur = NULL;
static bool connected = false; static bool connected = false;
static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL}; static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL};
static void* cb_ctx[IF_NUM_MAX]; static void* cb_ctx[IF_NUM_MAX];
UsbInterface usb_cdc_single = { FuriHalUsbInterface usb_cdc_single = {
.init = cdc_init, .init = cdc_init,
.deinit = cdc_deinit, .deinit = cdc_deinit,
.wakeup = cdc_on_wakeup, .wakeup = cdc_on_wakeup,
@ -413,7 +413,7 @@ UsbInterface usb_cdc_single = {
.cfg_descr = (void*)&cdc_cfg_desc_single, .cfg_descr = (void*)&cdc_cfg_desc_single,
}; };
UsbInterface usb_cdc_dual = { FuriHalUsbInterface usb_cdc_dual = {
.init = cdc_init, .init = cdc_init,
.deinit = cdc_deinit, .deinit = cdc_deinit,
.wakeup = cdc_on_wakeup, .wakeup = cdc_on_wakeup,
@ -428,7 +428,7 @@ UsbInterface usb_cdc_dual = {
.cfg_descr = (void*)&cdc_cfg_desc_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; usb_dev = dev;
cdc_if_cur = intf; cdc_if_cur = intf;

View File

@ -236,7 +236,7 @@ static struct HidReport {
struct HidReportConsumer consumer; struct HidReportConsumer consumer;
} __attribute__((packed)) hid_report; } __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_deinit(usbd_device* dev);
static void hid_on_wakeup(usbd_device* dev); static void hid_on_wakeup(usbd_device* dev);
static void hid_on_suspend(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); return hid_send_report(ReportIdConsumer);
} }
UsbInterface usb_hid = { FuriHalUsbInterface usb_hid = {
.init = hid_init, .init = hid_init,
.deinit = hid_deinit, .deinit = hid_deinit,
.wakeup = hid_on_wakeup, .wakeup = hid_on_wakeup,
@ -363,7 +363,7 @@ UsbInterface usb_hid = {
.cfg_descr = (void*)&hid_cfg_desc, .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); if(hid_semaphore == NULL) hid_semaphore = osSemaphoreNew(1, 1, NULL);
usb_dev = dev; usb_dev = dev;
hid_report.keyboard.report_id = ReportIdKeyboard; hid_report.keyboard.report_id = ReportIdKeyboard;

View File

@ -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_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_prod_desc = USB_STRING_DESC("U2F Token");
static const struct usb_string_descriptor dev_serial_desc = USB_STRING_DESC("TODO: serial");
/* Device descriptor */ /* Device descriptor */
static const struct usb_device_descriptor hid_u2f_device_desc = { 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), .bcdDevice = VERSION_BCD(1, 0, 0),
.iManufacturer = UsbDevManuf, .iManufacturer = UsbDevManuf,
.iProduct = UsbDevProduct, .iProduct = UsbDevProduct,
.iSerialNumber = UsbDevSerial, .iSerialNumber = 0,
.bNumConfigurations = 1, .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_deinit(usbd_device* dev);
static void hid_u2f_on_wakeup(usbd_device* dev); static void hid_u2f_on_wakeup(usbd_device* dev);
static void hid_u2f_on_suspend(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, .init = hid_u2f_init,
.deinit = hid_u2f_deinit, .deinit = hid_u2f_deinit,
.wakeup = hid_u2f_on_wakeup, .wakeup = hid_u2f_on_wakeup,
@ -181,12 +180,12 @@ UsbInterface usb_hid_u2f = {
.str_manuf_descr = (void*)&dev_manuf_desc, .str_manuf_descr = (void*)&dev_manuf_desc,
.str_prod_descr = (void*)&dev_prod_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, .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); if(hid_u2f_semaphore == NULL) hid_u2f_semaphore = osSemaphoreNew(1, 1, NULL);
usb_dev = dev; usb_dev = dev;

View File

@ -91,6 +91,10 @@ void furi_hal_power_enable_otg();
*/ */
void furi_hal_power_disable_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 /** Get OTG status
* *
* @return true if enabled * @return true if enabled

View File

@ -2,10 +2,10 @@
#include "usb.h" #include "usb.h"
typedef struct UsbInterface UsbInterface; typedef struct FuriHalUsbInterface FuriHalUsbInterface;
struct UsbInterface { struct FuriHalUsbInterface {
void (*init)(usbd_device* dev, UsbInterface* intf); void (*init)(usbd_device* dev, FuriHalUsbInterface* intf);
void (*deinit)(usbd_device* dev); void (*deinit)(usbd_device* dev);
void (*wakeup)(usbd_device* dev); void (*wakeup)(usbd_device* dev);
void (*suspend)(usbd_device* dev); void (*suspend)(usbd_device* dev);
@ -20,10 +20,19 @@ struct UsbInterface {
}; };
/** USB device interface modes */ /** USB device interface modes */
extern UsbInterface usb_cdc_single; extern FuriHalUsbInterface usb_cdc_single;
extern UsbInterface usb_cdc_dual; extern FuriHalUsbInterface usb_cdc_dual;
extern UsbInterface usb_hid; extern FuriHalUsbInterface usb_hid;
extern UsbInterface usb_hid_u2f; extern FuriHalUsbInterface usb_hid_u2f;
typedef enum {
FuriHalUsbStateEventReset,
FuriHalUsbStateEventWakeup,
FuriHalUsbStateEventSuspend,
FuriHalUsbStateEventDescriptorRequest,
} FuriHalUsbStateEvent;
typedef void (*FuriHalUsbStateCallback)(FuriHalUsbStateEvent state, void* context);
/** USB device low-level initialization /** USB device low-level initialization
*/ */
@ -33,13 +42,13 @@ void furi_hal_usb_init();
* *
* @param mode new USB device mode * @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 /** Get USB device configuration
* *
* @return current USB device mode * @return current USB device mode
*/ */
UsbInterface* furi_hal_usb_get_config(); FuriHalUsbInterface* furi_hal_usb_get_config();
/** Disable USB device /** Disable USB device
*/ */
@ -48,3 +57,11 @@ void furi_hal_usb_disable();
/** Enable USB device /** Enable USB device
*/ */
void furi_hal_usb_enable(); 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();

View File

@ -10,19 +10,6 @@ uint8_t bit_reverse(uint8_t b) {
return 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 { typedef struct {
REG00 r00; REG00 r00;
REG01 r01; REG01 r01;
@ -51,60 +38,101 @@ static bq25896_regs_t bq25896_regs;
void bq25896_init(FuriHalI2cBusHandle* handle) { void bq25896_init(FuriHalI2cBusHandle* handle) {
bq25896_regs.r14.REG_RST = 1; 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 // 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 // Poll ADC forever
bq25896_regs.r02.CONV_START = 1; bq25896_regs.r02.CONV_START = 1;
bq25896_regs.r02.CONV_RATE = 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_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) { void bq25896_poweroff(FuriHalI2cBusHandle* handle) {
bq25896_regs.r09.BATFET_DIS = 1; 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) { bool bq25896_is_charging(FuriHalI2cBusHandle* handle) {
bq25896_read(handle, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs)); furi_hal_i2c_read_mem(
bq25896_read_reg(handle, 0x0B, (uint8_t*)&bq25896_regs.r0B); 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; return bq25896_regs.r0B.CHRG_STAT != ChrgStatNo;
} }
void bq25896_enable_charging(FuriHalI2cBusHandle* handle) { void bq25896_enable_charging(FuriHalI2cBusHandle* handle) {
bq25896_regs.r03.CHG_CONFIG = 1; 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) { void bq25896_disable_charging(FuriHalI2cBusHandle* handle) {
bq25896_regs.r03.CHG_CONFIG = 0; 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) { void bq25896_enable_otg(FuriHalI2cBusHandle* handle) {
bq25896_regs.r03.OTG_CONFIG = 1; 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) { void bq25896_disable_otg(FuriHalI2cBusHandle* handle) {
bq25896_regs.r03.OTG_CONFIG = 0; 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) { 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; 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) { 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) { if(bq25896_regs.r11.VBUS_GD) {
return (uint16_t)bq25896_regs.r11.VBUSV * 100 + 2600; return (uint16_t)bq25896_regs.r11.VBUSV * 100 + 2600;
} else { } else {
@ -113,21 +141,25 @@ uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle) {
} }
uint16_t bq25896_get_vsys_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; return (uint16_t)bq25896_regs.r0F.SYSV * 20 + 2304;
} }
uint16_t bq25896_get_vbat_voltage(FuriHalI2cBusHandle* handle) { 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; return (uint16_t)bq25896_regs.r0E.BATV * 20 + 2304;
} }
uint16_t bq25896_get_vbat_current(FuriHalI2cBusHandle* handle) { 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; return (uint16_t)bq25896_regs.r12.ICHGR * 50;
} }
uint32_t bq25896_get_ntc_mpct(FuriHalI2cBusHandle* handle) { 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; return (uint32_t)bq25896_regs.r10.TSPCT * 465 + 21000;
} }

View File

@ -28,6 +28,9 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle);
/** Is otg enabled */ /** Is otg enabled */
bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle); bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle);
/** Check OTG BOOST Fault status */
bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle);
/** Get VBUS Voltage in mV */ /** Get VBUS Voltage in mV */
uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle); uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle);

View File

@ -8,24 +8,17 @@
#define TAG "Gauge" #define TAG "Gauge"
uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) {
uint8_t buffer[2] = {address}; uint16_t buf = 0;
uint16_t ret = 0;
if(furi_hal_i2c_trx(handle, BQ27220_ADDRESS, buffer, 1, buffer, 2, BQ27220_I2C_TIMEOUT)) { furi_hal_i2c_read_mem(
ret = *(uint16_t*)buffer; handle, BQ27220_ADDRESS, address, (uint8_t*)&buf, 2, BQ27220_I2C_TIMEOUT);
}
return ret; return buf;
} }
bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) { bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) {
bool ret = false; bool ret = furi_hal_i2c_write_mem(
uint8_t buffer[3]; handle, BQ27220_ADDRESS, CommandControl, (uint8_t*)&control, 2, BQ27220_I2C_TIMEOUT);
buffer[0] = CommandControl;
buffer[1] = control & 0xFF;
buffer[2] = (control >> 8) & 0xFF;
ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 3, BQ27220_I2C_TIMEOUT);
return ret; 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 bq27220_set_parameter_u16(FuriHalI2cBusHandle* handle, uint16_t address, uint16_t value) {
bool ret; bool ret;
uint8_t buffer[5]; uint8_t buffer[4];
buffer[0] = CommandSelectSubclass; buffer[0] = address & 0xFF;
buffer[1] = address & 0xFF; buffer[1] = (address >> 8) & 0xFF;
buffer[2] = (address >> 8) & 0xFF; buffer[2] = (value >> 8) & 0xFF;
buffer[3] = (value >> 8) & 0xFF; buffer[3] = value & 0xFF;
buffer[4] = value & 0xFF; ret = furi_hal_i2c_write_mem(
ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 5, BQ27220_I2C_TIMEOUT); handle, BQ27220_ADDRESS, CommandSelectSubclass, buffer, 4, BQ27220_I2C_TIMEOUT);
delay_us(10000); delay_us(10000);
uint8_t checksum = bq27220_get_checksum(&buffer[1], 4); uint8_t checksum = bq27220_get_checksum(&buffer[1], 4);
buffer[0] = CommandMACDataSum; buffer[0] = checksum;
buffer[1] = checksum; buffer[1] = 6;
buffer[2] = 6; ret = furi_hal_i2c_write_mem(
ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 3, BQ27220_I2C_TIMEOUT); handle, BQ27220_ADDRESS, CommandMACDataSum, buffer, 2, BQ27220_I2C_TIMEOUT);
delay_us(10000); delay_us(10000);
return ret; return ret;

View File

@ -1,21 +1,17 @@
#include "lp5562.h" #include "lp5562.h"
#include "lp5562_reg.h" #include "lp5562_reg.h"
#include <furi_hal.h>
#include <stdio.h> #include <stdio.h>
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) { void lp5562_reset(FuriHalI2cBusHandle* handle) {
Reg0D_Reset reg = {.value = 0xFF}; Reg0D_Reset reg = {.value = 0xFF};
lp5562_write_reg(handle, 0x0D, (uint8_t*)&reg); furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x0D, *(uint8_t*)&reg, LP5562_I2C_TIMEOUT);
} }
void lp5562_configure(FuriHalI2cBusHandle* handle) { void lp5562_configure(FuriHalI2cBusHandle* handle) {
Reg08_Config config = {.INT_CLK_EN = true, .PS_EN = true, .PWM_HF = true}; 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 = { Reg70_LedMap map = {
.red = EngSelectI2C, .red = EngSelectI2C,
@ -23,42 +19,178 @@ void lp5562_configure(FuriHalI2cBusHandle* handle) {
.blue = EngSelectI2C, .blue = EngSelectI2C,
.white = 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) { void lp5562_enable(FuriHalI2cBusHandle* handle) {
Reg00_Enable reg = {.CHIP_EN = true, .LOG_EN = true}; Reg00_Enable reg = {.CHIP_EN = true, .LOG_EN = true};
lp5562_write_reg(handle, 0x00, (uint8_t*)&reg); furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, *(uint8_t*)&reg, 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) { void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) {
uint8_t reg_no; uint8_t reg_no;
if(channel == LP5562ChannelRed) { if(channel == LP5562ChannelRed) {
reg_no = 0x07; reg_no = LP5562_CHANNEL_RED_CURRENT_REGISTER;
} else if(channel == LP5562ChannelGreen) { } else if(channel == LP5562ChannelGreen) {
reg_no = 0x06; reg_no = LP5562_CHANNEL_GREEN_CURRENT_REGISTER;
} else if(channel == LP5562ChannelBlue) { } else if(channel == LP5562ChannelBlue) {
reg_no = 0x05; reg_no = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;
} else if(channel == LP5562ChannelWhite) { } else if(channel == LP5562ChannelWhite) {
reg_no = 0x0F; reg_no = LP5562_CHANNEL_WHITE_CURRENT_REGISTER;
} else { } else {
return; 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) { void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) {
uint8_t reg_no; uint8_t reg_no;
if(channel == LP5562ChannelRed) { if(channel == LP5562ChannelRed) {
reg_no = 0x04; reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER;
} else if(channel == LP5562ChannelGreen) { } else if(channel == LP5562ChannelGreen) {
reg_no = 0x03; reg_no = LP5562_CHANNEL_GREEN_VALUE_REGISTER;
} else if(channel == LP5562ChannelBlue) { } else if(channel == LP5562ChannelBlue) {
reg_no = 0x02; reg_no = LP5562_CHANNEL_BLUE_VALUE_REGISTER;
} else if(channel == LP5562ChannelWhite) { } else if(channel == LP5562ChannelWhite) {
reg_no = 0x0E; reg_no = LP5562_CHANNEL_WHITE_VALUE_REGISTER;
} else { } else {
return; 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, &reg_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, &reg_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, &reg_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);
} }

View File

@ -12,6 +12,13 @@ typedef enum {
LP5562ChannelWhite, LP5562ChannelWhite,
} LP5562Channel; } LP5562Channel;
typedef enum {
LP5562Direct = 0,
LP5562Engine1 = 1,
LP5562Engine2 = 2,
LP5562Engine3 = 3,
} LP5562Engine;
/** Initialize Driver */ /** Initialize Driver */
void lp5562_reset(FuriHalI2cBusHandle* handle); void lp5562_reset(FuriHalI2cBusHandle* handle);
@ -24,5 +31,24 @@ void lp5562_enable(FuriHalI2cBusHandle* handle);
/** Set channel current */ /** Set channel current */
void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value); 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); 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);

View File

@ -7,6 +7,16 @@
#define LP5562_ADDRESS 0x60 #define LP5562_ADDRESS 0x60
#define LP5562_I2C_TIMEOUT 50 #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 { typedef enum {
EngExecHold = 0b00, EngExecHold = 0b00,
EngExecStep = 0b01, EngExecStep = 0b01,

View File

@ -4,6 +4,7 @@ import os
import re import re
import shutil import shutil
import subprocess import subprocess
import multiprocessing
from flipper.app import App from flipper.app import App
@ -60,16 +61,29 @@ class Main(App):
output.append(os.path.join(dirpath, filename)) output.append(os.path.join(dirpath, filename))
return output 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): def _format_sources(self, sources: list, dry_run: bool = False):
args = ["clang-format", "--Werror", "--style=file", "-i"] args = ["clang-format", "--Werror", "--style=file", "-i"]
if dry_run: if dry_run:
args.append("--dry-run") args.append("--dry-run")
args += sources
try: files_per_task = 69
subprocess.check_call(args) tasks = []
return True while len(sources) > 0:
except subprocess.CalledProcessError as e: tasks.append(args + sources[:files_per_task])
return False 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): def _fix_filename(self, filename: str):
return filename.replace("-", "_") return filename.replace("-", "_")