Kaynağa Gözat

Power: bq25896 and bq27220 drivers, portable power api. (#224)

* Power: add portability layer, port to F3.
* F3: bq25896 driver stab
* GUI: new primary and secondary fonts
* F3: bq25896 register map, basic functions
* F3: move bq27220 command descritpion to separate file. Code format.
* Power: OTG enable/disable
* Power: choose more correct profile
* F3: pet bq25896 watchdog sometimes
あく 5 yıl önce
ebeveyn
işleme
f05ffddbde

+ 2 - 2
applications/gui/canvas.c

@@ -120,9 +120,9 @@ void canvas_font_set(CanvasApi* api, Font font) {
     Canvas* canvas = (Canvas*)api;
     u8g2_SetFontMode(&canvas->fb, 1);
     if(font == FontPrimary) {
-        u8g2_SetFont(&canvas->fb, u8g2_font_Born2bSportyV2_tr);
+        u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tf);
     } else if(font == FontSecondary) {
-        u8g2_SetFont(&canvas->fb, u8g2_font_HelvetiPixel_tr);
+        u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr);
     } else {
         furi_check(0);
     }

+ 37 - 40
applications/power/power.c

@@ -1,15 +1,13 @@
 #include "power.h"
 
 #include <flipper_v2.h>
+
+#include <menu/menu.h>
+#include <menu/menu_item.h>
 #include <gui/gui.h>
 #include <gui/widget.h>
 #include <assets_icons.h>
-
-#define BATTERY_MIN_VOLTAGE 3.2f
-#define BATTERY_MAX_VOLTAGE 4.0f
-#define BATTERY_INIT 0xFFAACCEE
-
-extern ADC_HandleTypeDef hadc1;
+#include <api-hal-power.h>
 
 struct Power {
     Icon* usb_icon;
@@ -18,7 +16,10 @@ struct Power {
     Icon* battery_icon;
     Widget* battery_widget;
 
-    uint32_t charge;
+    ValueMutex* menu_vm;
+    MenuItem* menu;
+
+    uint8_t charge;
 };
 
 void power_draw_usb_callback(CanvasApi* canvas, void* context) {
@@ -32,40 +33,40 @@ void power_draw_battery_callback(CanvasApi* canvas, void* context) {
     Power* power = context;
 
     canvas->draw_icon(canvas, 0, 0, power->battery_icon);
-
-    if(power->charge != BATTERY_INIT) {
-        float charge = ((float)power->charge / 1000 * 2 - BATTERY_MIN_VOLTAGE) /
-                       (BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE);
-        if(charge > 1) {
-            charge = 1;
-        }
-        canvas->draw_box(canvas, 2, 2, charge * 14, 4);
-    }
+    canvas->draw_box(canvas, 2, 2, (float)power->charge / 100 * 14, 4);
 }
 
-void power_input_events_callback(const void* value, void* ctx) {
-    assert(ctx);
-    Power* power = ctx;
-    const InputEvent* event = value;
+void power_off_callback(void* context) {
+    api_hal_power_off();
+}
 
-    if(event->input != InputCharging) return;
+void power_enable_otg_callback(void* context) {
+    api_hal_power_enable_otg();
+}
 
-    widget_enabled_set(power->usb_widget, event->state);
-    widget_update(power->usb_widget);
+void power_disable_otg_callback(void* context) {
+    api_hal_power_disable_otg();
 }
 
 Power* power_alloc() {
     Power* power = furi_alloc(sizeof(Power));
 
+    power->menu_vm = furi_open("menu");
+    furi_check(power->menu_vm);
+
+    power->menu = menu_item_alloc_menu("Power", NULL);
+    menu_item_subitem_add(
+        power->menu, menu_item_alloc_function("Poweroff", NULL, power_off_callback, power));
+    menu_item_subitem_add(
+        power->menu,
+        menu_item_alloc_function("Enable OTG", NULL, power_enable_otg_callback, power));
+    menu_item_subitem_add(
+        power->menu,
+        menu_item_alloc_function("Disable OTG", NULL, power_disable_otg_callback, power));
+
     power->usb_icon = assets_icons_get(I_USBConnected_15x8);
     power->usb_widget = widget_alloc();
     widget_set_width(power->usb_widget, icon_get_width(power->usb_icon));
-
-    ValueManager* input_state_manager = furi_open("input_state");
-    InputState input_state;
-    read_mutex_block(&input_state_manager->value, &input_state, sizeof(input_state));
-    widget_enabled_set(power->usb_widget, input_state.charging);
-
     widget_draw_callback_set(power->usb_widget, power_draw_usb_callback, power);
 
     power->battery_icon = assets_icons_get(I_Battery_19x8);
@@ -73,12 +74,6 @@ Power* power_alloc() {
     widget_set_width(power->battery_widget, icon_get_width(power->battery_icon));
     widget_draw_callback_set(power->battery_widget, power_draw_battery_callback, power);
 
-    PubSub* input_event_record = furi_open("input_events");
-    assert(input_event_record);
-    subscribe_pubsub(input_event_record, power_input_events_callback, power);
-
-    power->charge = BATTERY_INIT;
-
     return power;
 }
 
@@ -99,19 +94,21 @@ void power_task(void* p) {
     gui->add_widget(gui, power->battery_widget, GuiLayerStatusBarRight);
     furi_commit(gui_record);
 
+    with_value_mutex(
+        power->menu_vm, (Menu * menu) { menu_item_add(menu, power->menu); });
+
     if(!furi_create("power", power)) {
         printf("[power_task] unable to create power record\n");
         furiac_exit(NULL);
     }
 
+    api_hal_power_init();
+
     furiac_ready();
 
     while(1) {
-        HAL_ADC_Start(&hadc1);
-        if(HAL_ADC_PollForConversion(&hadc1, 1000) != HAL_TIMEOUT) {
-            power->charge = HAL_ADC_GetValue(&hadc1);
-            widget_update(power->battery_widget);
-        }
+        power->charge = api_hal_power_get_pct();
+        widget_enabled_set(power->usb_widget, api_hal_power_is_charging());
         osDelay(1000);
     }
 }

+ 1 - 0
firmware/Makefile

@@ -14,6 +14,7 @@ include			$(PROJECT_ROOT)/lib/lib.mk
 TARGET			?= f2
 TARGET_DIR		= targets/$(TARGET)
 include			$(TARGET_DIR)/target.mk
+CFLAGS			+= -Itargets/Inc
 
 include			$(PROJECT_ROOT)/make/git.mk
 include			$(PROJECT_ROOT)/make/toolchain.mk

+ 22 - 0
firmware/targets/Inc/api-hal-power.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* Initialize drivers */
+void api_hal_power_init();
+
+/* Get predicted remaining battery capacity in percents */
+uint8_t api_hal_power_get_pct();
+
+/* Get charging status */
+bool api_hal_power_is_charging();
+
+/* Poweroff system */
+void api_hal_power_off();
+
+/* OTG enable */
+void api_hal_power_enable_otg();
+
+/* OTG disable */
+void api_hal_power_disable_otg();

+ 34 - 0
firmware/targets/f2/api-hal/api-hal-power.c

@@ -0,0 +1,34 @@
+#include <api-hal-power.h>
+#include <adc.h>
+
+#define BATTERY_MIN_VOLTAGE 3.2f
+#define BATTERY_MAX_VOLTAGE 4.0f
+
+void api_hal_power_init() {}
+
+uint8_t api_hal_power_get_pct() {
+    float value;
+    HAL_ADC_Start(&hadc1);
+    if(HAL_ADC_PollForConversion(&hadc1, 1000) != HAL_TIMEOUT) {
+        value = HAL_ADC_GetValue(&hadc1);
+    }
+
+    value = ((float)value / 10 * 2 - BATTERY_MIN_VOLTAGE) /
+                       (BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE);
+
+    if(value > 100) {
+        value = 100;
+    }
+
+    return value;
+}
+
+bool api_hal_power_is_charging() {
+    return false;
+}
+
+void api_hal_power_off() {}
+
+void api_hal_power_enable_otg() {}
+
+void api_hal_power_disable_otg() {}

+ 18 - 0
firmware/targets/f3/Inc/bq25896.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <stdbool.h>
+
+/* Initialize Driver */
+void bq25896_init();
+
+/* Send device into shipping mode */
+void bq25896_poweroff();
+
+/* Is currently charging */
+bool bq25896_is_charging();
+
+/* Enable otg */
+void bq25896_enable_otg();
+
+/* Disable otg */
+void bq25896_disable_otg();

+ 263 - 0
firmware/targets/f3/Inc/bq25896_reg.h

@@ -0,0 +1,263 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#if BITS_BIG_ENDIAN == 1
+#error Bit structures defined in this file is not portable to BE
+#endif
+
+#define BQ25896_ADDRESS 0xD6
+
+typedef struct {
+    uint8_t IINLIM:6;       // Input Current Limit, mA, offset: +100mA
+    bool EN_ILIM:1;         // Enable ILIM Pin
+    bool EN_HIZ:1;          // Enable HIZ Mode
+} REG00;
+
+#define IILIM_1600          (1<<5)
+#define IILIM_800           (1<<4)
+#define IILIM_400           (1<<3)
+#define IILIM_200           (1<<2)
+#define IILIM_100           (1<<1)
+#define IILIM_50            (1<<0)
+
+typedef struct {
+    uint8_t VINDPM_OS:5;    // Input Voltage Limit Offset, mV
+    bool BCOLD:1;           // Boost Mode Cold Temperature Monitor Threshold
+    uint8_t BHOT:2;         // Boost Mode Hot Temperature Monitor Threshold
+} REG01;
+
+#define VINDPM_OS_1600      (1<<4)
+#define VINDPM_OS_800       (1<<3)
+#define VINDPM_OS_400       (1<<2)
+#define VINDPM_OS_200       (1<<1)
+#define VINDPM_OS_100       (1<<0)
+
+
+typedef struct {
+    bool AUTO_DPDM_EN:1;    // Automatic Input Detection Enable
+    bool FORCE_DPDM:1;      // Force Input Detection
+    uint8_t RES:2;          // Reserved
+    bool ICO_EN:1;          // Input Current Optimizer (ICO) Enable
+    bool BOOST_FREQ:1;      // Boost Mode Frequency Selection
+    bool CONV_RATE:1;       // ADC Conversion Rate Selection
+    bool CONV_START:1;      // ADC Conversion Start Control
+} REG02;
+
+
+typedef struct {
+    bool MIN_VBAT_SEL:1;    // Minimum Battery Voltage (falling) to exit boost mode
+    uint8_t SYS_MIN:3;      // Minimum System Voltage Limit, mV, offset: +3000mV
+    bool CHG_CONFIG:1;      // Charge Enable Configuration
+    bool OTG_CONFIG:1;      // Boost (OTG) Mode Configuration
+    bool WD_RST:1;          // I2C Watchdog Timer Reset
+    bool BAT_LOADEN:1;      // Battery Load (IBATLOAD) Enable
+} REG03;
+
+#define SYS_MIN_400         (1<<2)
+#define SYS_MIN_200         (1<<1)
+#define SYS_MIN_100         (1<<0)
+
+typedef struct {
+    uint8_t ICHG:7;         // Fast Charge Current Limit, mA
+    bool EN_PUMPX:1;        // Current pulse control Enable
+} REG04;
+
+#define ICHG_4096           (1<<6)
+#define ICHG_2048           (1<<5)
+#define ICHG_1024           (1<<4)
+#define ICHG_512            (1<<3)
+#define ICHG_256            (1<<2)
+#define ICHG_128            (1<<1)
+#define ICHG_64             (1<<0)
+
+typedef struct {
+    uint8_t ITERM:4;        // Termination Current Limit, offset: +64mA
+    uint8_t IPRECHG:4;      // Precharge Current Limit, offset: +64mA
+} REG05;
+
+#define IPRETERM_512        (1<<3)
+#define IPRETERM_256        (1<<2)
+#define IPRETERM_128        (1<<1)
+#define IPRETERM_64         (1<<0)
+
+typedef struct {
+    bool VRECHG:1;          // Battery Recharge Threshold Offset
+    bool BATLOWV:1;         // Battery Precharge to Fast Charge Threshold
+    uint8_t VREG:6;         // Charge Voltage Limit, offset: +3840mV
+} REG06;
+
+#define VREG_512            (1<<5)
+#define VREG_256            (1<<4)
+#define VREG_128            (1<<3)
+#define VREG_64             (1<<2)
+#define VREG_32             (1<<1)
+#define VREG_16             (1<<0)
+
+typedef struct {
+    bool JEITA_ISET:1;      // JEITA Low Temperature Current Setting
+    uint8_t CHG_TIMER:2;    // Fast Charge Timer Setting
+    bool EN_TIMER:1;        // Charging Safety Timer Enable
+    uint8_t WATCHDOG:2;     // I2C Watchdog Timer Setting
+    bool STAT_DIS:1;        // STAT Pin Disable
+    bool EN_TERM:1;         // Charging Termination Enable
+} REG07;
+
+#define WATCHDOG_DIS        (0b00)
+#define WATCHDOG_40         (0b01)
+#define WATCHDOG_80         (0b10)
+#define WATCHDOG_160        (0b11)
+
+#define CHG_TIMER_5         (0b00)
+#define CHG_TIMER_8         (0b01)
+#define CHG_TIMER_12        (0b10)
+#define CHG_TIMER_20        (0b11)
+
+typedef struct {
+    uint8_t TREG:2;         // Thermal Regulation Threshold
+    uint8_t VCLAMP:3;       // IR Compensation Voltage Clamp
+    uint8_t BAT_COMP:3;     // IR Compensation Resistor Setting 
+} REG08;
+
+#define BAT_COMP_80         (1<<2)
+#define BAT_COMP_40         (1<<1)
+#define BAT_COMP_20         (1<<0)
+
+#define VCLAMP_128          (1<<2)
+#define VCLAMP_64           (1<<1)
+#define VCLAMP_32           (1<<0)
+
+#define TREG_60             (0b00)
+#define TREG_80             (0b01)
+#define TREG_100            (0b10)
+#define TREG_120            (0b11)
+
+typedef struct {
+    bool PUMPX_DN:1;        // Current pulse control voltage down enable
+    bool PUMPX_UP:1;        // Current pulse control voltage up enable
+    bool BATFET_RST_EN:1;   // BATFET full system reset enable
+    bool BATFET_DLY:1;      // BATFET turn off delay control
+    bool JEITA_VSET:1;      // JEITA High Temperature Voltage Setting
+    bool BATFET_DIS:1;      // Force BATFET off to enable ship mode
+    bool TMR2X_EN:1;        // Safety Timer Setting during DPM or Thermal Regulation
+    bool FORCE_ICO:1;       // Force Start Input Current Optimizer 
+} REG09;
+
+typedef struct {
+    uint8_t BOOST_LIM:3;    // Boost Mode Current Limit
+    bool PFM_OTG_DIS:1;     // PFM mode allowed in boost mode
+    uint8_t BOOSTV:4;       // Boost Mode Voltage Regulation, offset: +4550mV
+} REG0A;
+
+#define BOOSTV_512          (1<<3)
+#define BOOSTV_256          (1<<2)
+#define BOOSTV_128          (1<<1)
+#define BOOSTV_64           (1<<0)
+
+#define BOOST_LIM_500       (0b000)
+#define BOOST_LIM_750       (0b001)
+#define BOOST_LIM_1200      (0b010)
+#define BOOST_LIM_1400      (0b011)
+#define BOOST_LIM_1650      (0b100)
+#define BOOST_LIM_1875      (0b101)
+#define BOOST_LIM_2150      (0b110)
+#define BOOST_LIM_RSVD      (0b111)
+
+
+typedef enum {
+    VBusStatNo = 0b000,
+    VBusStatUSB = 0b001,
+    VBusStatExternal = 0b010,
+    VBusStatOTG = 0b111,
+} VBusStat;
+
+typedef enum {
+    ChrgStatNo = 0b00,
+    ChrgStatPre = 0b01,
+    ChrgStatFast = 0b10,
+    ChrgStatDone = 0b11,
+} ChrgStat;
+
+typedef struct {
+    bool VSYS_STAT:1;       // VSYS Regulation Status
+    bool RES:1;             // Reserved: Always reads 1
+    bool PG_STAT:1;         // Power Good Status
+    ChrgStat CHRG_STAT:2;    // Charging Status
+    VBusStat VBUS_STAT:3;    // VBUS Status register
+} REG0B;
+
+typedef enum {
+    ChrgFaultNO = 0b00,
+    ChrgFaultIN = 0b01,
+    ChrgFaultTH = 0b10,
+    ChrgFaultTIM = 0b11,
+} ChrgFault;
+
+typedef enum {
+    NtcFaultNo = 0b000,
+    NtcFaultWarm = 0b010,
+    NtcFaultCool = 0b011,
+    NtcFaultCold = 0b101,
+    NtcFaultHot = 0b110,
+} NtcFault;
+
+typedef struct {
+    NtcFault NTC_FAULT:3;    // NTC Fault Status
+    bool BAT_FAULT:1;       // Battery Fault Status
+    ChrgFault CHRG_FAULT:2;   // Charge Fault Status
+    bool BOOST_FAULT:1;     // Boost Mode Fault Status
+    bool WATCHDOG_FAULT:1;  // Watchdog Fault Status
+} REG0C;
+
+typedef struct {
+    uint8_t VINDPM:7;       // Absolute VINDPM Threshold, offset: +2600mV
+    bool FORCE_VINDPM:1;    // VINDPM Threshold Setting Method
+} REG0D;
+
+#define VINDPM_6400         (1<<6)
+#define VINDPM_3200         (1<<5)
+#define VINDPM_1600         (1<<4)
+#define VINDPM_800          (1<<3)
+#define VINDPM_400          (1<<2)
+#define VINDPM_200          (1<<1)
+#define VINDPM_100          (1<<0)
+
+typedef struct {
+    uint8_t BATV:7;         // ADC conversion of Battery Voltage (VBAT), offset: +2304mV
+    bool THERM_STAT:1;      // Thermal Regulation Status
+} REG0E;
+
+typedef struct {
+    uint8_t SYSV:7;         // ADDC conversion of System Voltage (VSYS), offset: +2304mV
+    uint8_t RES:1;          // Reserved: Always reads 0
+} REG0F;
+
+typedef struct {
+    uint8_t TSPCT:7;        // ADC conversion of TS Voltage (TS) as percentage of REGN, offset: +21%
+    uint8_t RES:1;          // Reserved: Always reads 0
+} REG10;
+
+typedef struct {
+    uint8_t VBUSV:7;        // ADC conversion of VBUS voltage (VBUS), offset: +2600mV
+    bool VBUS_GD:1;         // VBUS Good Status
+} REG11;
+
+typedef struct {
+    uint8_t ICHGR:7;        // ADC conversion of Charge Current (IBAT) when VBAT > VBATSHORT
+    uint8_t RES:1;          // Reserved: Always reads 0
+} REG12;
+
+typedef struct {
+    uint8_t IDPM_LIM:6;     // Input Current Limit in effect while Input Current Optimizer (ICO) is enabled, offset: 100mA (default)
+    bool IDPM_STAT:1;       // IINDPM Status
+    bool VDPM_STAT:1;       // VINDPM Status
+} REG13;
+
+typedef struct {
+    uint8_t DEV_REV:2;      // Device Revision
+    bool TS_PROFILE:1;      // Temperature Profile
+    uint8_t PN:3;           // Device Configuration
+    bool ICO_OPTIMIZED:1;   // Input Current Optimizer (ICO) Status
+    bool REG_RST:1;         // Register Reset
+} REG14;

+ 21 - 0
firmware/targets/f3/Inc/bq27220.h

@@ -0,0 +1,21 @@
+#pragma once
+
+#include <stdint.h>
+
+/* Initialize Driver */
+void bq27220_init();
+
+/* Get battery voltage in mV */
+uint16_t bq27220_get_voltage();
+
+/* Get current in mA */
+int16_t bq27220_get_current();
+
+/* Get compensated full charge capacity in in mAh */
+uint16_t bq27220_get_full_charge_capacity();
+
+/* Get remaining capacity in in mAh */
+uint16_t bq27220_get_remaining_capacity();
+
+/* Get predicted remaining battery capacity in percents */
+uint16_t bq27220_get_state_of_charge();

+ 66 - 0
firmware/targets/f3/Inc/bq27220_reg.h

@@ -0,0 +1,66 @@
+#pragma once
+
+#define BQ27220_ADDRESS 0xAA
+
+#define CommandControl 0x00
+#define CommandAtRate 0x02
+#define CommandAtRateTimeToEmpty 0x04
+#define CommandTemperature 0x06
+#define CommandVoltage 0x08
+#define CommandBatteryStatus 0x0A
+#define CommandCurrent 0x0C
+#define CommandRemainingCapacity 0x10
+#define CommandFullChargeCapacity 0x12
+#define CommandAverageCurrent 0x14
+#define CommandTimeToEmpty 0x16
+#define CommandTimeToFull 0x18
+#define CommandStandbyCurrent 0x1A
+#define CommandStandbyTimeToEmpty 0x1C
+#define CommandMaxLoadCurrent 0x1E
+#define CommandMaxLoadTimeToEmpty 0x20
+#define CommandRawCoulombCount 0x22
+#define CommandAveragePower 0x24
+#define CommandInternalTemperature 0x28
+#define CommandCycleCount 0x2A
+#define CommandStateOfCharge 0x2C
+#define CommandStateOfHealth 0x2E
+#define CommandChargeVoltage 0x30
+#define CommandChargeCurrent 0x32
+#define CommandBTPDischargeSet 0x34
+#define CommandBTPChargeSet 0x36
+#define CommandOperationStatus 0x3A
+#define CommandDesignCapacity 0x3C
+#define CommandMACData 0x40
+#define CommandMACDataSum 0x60
+#define CommandMACDataLen 0x61
+#define CommandAnalogCount 0x79
+#define CommandRawCurrent 0x7A
+#define CommandRawVoltage 0x7C
+#define CommandRawIntTemp 0x7E
+
+#define Control_CONTROL_STATUS 0x0000
+#define Control_DEVICE_NUMBER 0x0001
+#define Control_FW_VERSION 0x0002
+#define Control_BOARD_OFFSET 0x0009
+#define Control_CC_OFFSET 0x000A
+#define Control_CC_OFFSET_SAVE 0x000B
+#define Control_OCV_CMD 0x000C
+#define Control_BAT_INSERT 0x000D
+#define Control_BAT_REMOVE 0x000E
+#define Control_SET_SNOOZE 0x0013
+#define Control_CLEAR_SNOOZE 0x0014
+#define Control_SET_PROFILE_1 0x0015
+#define Control_SET_PROFILE_2 0x0016
+#define Control_SET_PROFILE_3 0x0017
+#define Control_SET_PROFILE_4 0x0018
+#define Control_SET_PROFILE_5 0x0019
+#define Control_SET_PROFILE_6 0x001A
+#define Control_CAL_TOGGLE 0x002D
+#define Control_SEALED 0x0030
+#define Control_RESET 0x0041
+#define Control_EXIT_CAL 0x0080
+#define Control_ENTER_CAL 0x0081
+#define Control_ENTER_CFG_UPDATE 0x0090
+#define Control_EXIT_CFG_UPDATE_REINIT 0x0091
+#define Control_EXIT_CFG_UPDATE 0x0092
+#define Control_RETURN_TO_ROM 0x0F00

+ 2 - 0
firmware/targets/f3/Inc/i2c.h

@@ -34,6 +34,8 @@ extern I2C_HandleTypeDef hi2c1;
 
 /* USER CODE BEGIN Private defines */
 
+#define POWER_I2C hi2c1
+
 /* USER CODE END Private defines */
 
 void MX_I2C1_Init(void);

+ 99 - 0
firmware/targets/f3/Src/bq25896.c

@@ -0,0 +1,99 @@
+
+#include <bq25896.h>
+#include <bq25896_reg.h>
+#include <i2c.h>
+
+uint8_t bit_reverse(uint8_t b) {
+   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
+   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
+   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
+   return b;
+}
+
+bool bq25896_read(uint8_t address, uint8_t* data, size_t size) {
+    if (HAL_I2C_Master_Transmit(&POWER_I2C, BQ25896_ADDRESS, &address, 1, 2000) != HAL_OK) {
+        return false;
+    }
+    if (HAL_I2C_Master_Receive(&POWER_I2C, BQ25896_ADDRESS, data, size, 2000) != HAL_OK) {
+        return false;
+    }
+    return true;
+}
+
+bool bq25896_read_reg(uint8_t address, uint8_t* data) {
+    bq25896_read(address, data, 1);
+    return true;
+}
+
+bool bq25896_write_reg(uint8_t address, uint8_t* data) {
+    uint8_t buffer[2] = { address, *data };
+
+    if (HAL_I2C_Master_Transmit(&POWER_I2C, BQ25896_ADDRESS, buffer, 2, 2000) != HAL_OK) {
+        return false;
+    }
+    return true;
+}
+
+typedef struct {
+    REG00 r00;
+    REG01 r01;
+    REG02 r02;
+    REG03 r03;
+    REG04 r04;
+    REG05 r05;
+    REG06 r06;
+    REG07 r07;
+    REG08 r08;
+    REG09 r09;
+    REG0A r0A;
+    REG0B r0B;
+    REG0C r0C;
+    REG0D r0D;
+    REG0E r0E;
+    REG0F r0F;
+    REG10 r10;
+    REG11 r11;
+    REG12 r12;
+    REG13 r13;
+    REG14 r14;
+} bq25896_regs_t;
+
+static bq25896_regs_t bq25896_regs;
+
+void bq25896_init() {
+    bq25896_read(0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs));
+
+    bq25896_regs.r09.BATFET_DIS = 0;
+    bq25896_write_reg(0x09, (uint8_t*)&bq25896_regs.r09);
+
+    bq25896_regs.r14.REG_RST = 1;
+    bq25896_write_reg(0x14, (uint8_t*)&bq25896_regs.r14);
+
+    // bq25896_regs.r07.WATCHDOG = 0b00;
+    // bq25896_write_reg(0x07, (uint8_t*)&bq25896_regs.r07);
+
+    bq25896_read(0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs));
+}
+
+void bq25896_poweroff() {
+    bq25896_regs.r09.BATFET_DIS=1;
+    bq25896_write_reg(0x09, (uint8_t*)&bq25896_regs.r09);
+}
+
+bool bq25896_is_charging() {
+    bq25896_regs.r03.WD_RST = 1;
+    bq25896_write_reg(0x03, (uint8_t*)&bq25896_regs.r03);
+
+    bq25896_read_reg(0x0B, (uint8_t*)&bq25896_regs.r0B);
+    return bq25896_regs.r0B.CHRG_STAT != ChrgStatNo;
+}
+
+void bq25896_enable_otg() {
+    bq25896_regs.r03.OTG_CONFIG = 1;
+    bq25896_write_reg(0x09, (uint8_t*)&bq25896_regs.r03);
+}
+
+void bq25896_disable_otg() {
+    bq25896_regs.r03.OTG_CONFIG = 0;
+    bq25896_write_reg(0x09, (uint8_t*)&bq25896_regs.r03);
+}

+ 53 - 0
firmware/targets/f3/Src/bq27220.c

@@ -0,0 +1,53 @@
+#include <bq27220.h>
+#include <bq27220_reg.h>
+#include <i2c.h>
+#include <stdbool.h>
+
+uint16_t bq27220_read_word(uint8_t address) {
+    uint8_t data[2] = { address };
+    if (HAL_I2C_Master_Transmit(&POWER_I2C, BQ27220_ADDRESS, data, 1, 2000) != HAL_OK) {
+        return 0;
+    }
+
+    if (HAL_I2C_Master_Receive(&POWER_I2C, BQ27220_ADDRESS, data, 2, 2000) != HAL_OK) {
+        return 0;
+    }
+    return *(uint16_t*)data;
+}
+
+bool bq27220_control(uint16_t control) {
+    uint8_t data[3] = { CommandControl };
+    data[1] = (control>>8) & 0xFF;
+    data[2] = control & 0xFF;
+    if (HAL_I2C_Master_Transmit(&POWER_I2C, BQ27220_ADDRESS, data, 3, 2000) != HAL_OK) {
+        return false;
+    }
+
+    return true;
+}
+
+void bq27220_init() {
+    bq27220_control(Control_ENTER_CFG_UPDATE);
+    bq27220_control(Control_SET_PROFILE_2);
+    bq27220_control(Control_EXIT_CFG_UPDATE);
+}
+
+uint16_t bq27220_get_voltage() {
+    return bq27220_read_word(CommandVoltage);
+}
+
+int16_t bq27220_get_current() {
+    return (int16_t)bq27220_read_word(CommandCurrent);
+}
+
+uint16_t bq27220_get_full_charge_capacity() {
+    return bq27220_read_word(CommandFullChargeCapacity);
+}
+
+uint16_t bq27220_get_remaining_capacity() {
+    return bq27220_read_word(CommandRemainingCapacity);
+}
+
+uint16_t bq27220_get_state_of_charge() {
+    return bq27220_read_word(CommandStateOfCharge);
+}

+ 28 - 0
firmware/targets/f3/api-hal/api-hal-power.c

@@ -0,0 +1,28 @@
+#include <api-hal-power.h>
+#include <bq27220.h>
+#include <bq25896.h>
+
+void api_hal_power_init() {
+    bq27220_init();
+    bq25896_init();
+}
+
+uint8_t api_hal_power_get_pct() {
+    return bq27220_get_state_of_charge();
+}
+
+bool api_hal_power_is_charging() {
+    return bq25896_is_charging();
+}
+
+void api_hal_power_off() {
+    bq25896_poweroff();
+}
+
+void api_hal_power_enable_otg() {
+    bq25896_enable_otg();
+}
+
+void api_hal_power_disable_otg() {
+    bq25896_disable_otg();
+}