furi_hal_power.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. #include <furi_hal_power.h>
  2. #include <furi_hal_clock.h>
  3. #include <furi_hal_bt.h>
  4. #include <furi_hal_resources.h>
  5. #include <furi_hal_uart.h>
  6. #include <stm32wbxx_ll_rcc.h>
  7. #include <stm32wbxx_ll_pwr.h>
  8. #include <stm32wbxx_ll_hsem.h>
  9. #include <stm32wbxx_ll_cortex.h>
  10. #include <stm32wbxx_ll_gpio.h>
  11. #include <hw_conf.h>
  12. #include <bq27220.h>
  13. #include <bq25896.h>
  14. #include <furi.h>
  15. #define TAG "FuriHalPower"
  16. #ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED
  17. #define FURI_HAL_POWER_DEEP_INSOMNIA 0
  18. #else
  19. #define FURI_HAL_POWER_DEEP_INSOMNIA 1
  20. #endif
  21. typedef struct {
  22. volatile uint8_t insomnia;
  23. volatile uint8_t deep_insomnia;
  24. volatile uint8_t suppress_charge;
  25. uint8_t gauge_initialized;
  26. uint8_t charger_initialized;
  27. } FuriHalPower;
  28. static volatile FuriHalPower furi_hal_power = {
  29. .insomnia = 0,
  30. .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA,
  31. .suppress_charge = 0,
  32. };
  33. const ParamCEDV cedv = {
  34. .cedv_conf.gauge_conf =
  35. {
  36. .CCT = 1,
  37. .CSYNC = 0,
  38. .EDV_CMP = 0,
  39. .SC = 1,
  40. .FIXED_EDV0 = 1,
  41. .FCC_LIM = 1,
  42. .FC_FOR_VDQ = 1,
  43. .IGNORE_SD = 1,
  44. .SME0 = 0,
  45. },
  46. .full_charge_cap = 2101,
  47. .design_cap = 2101,
  48. .EDV0 = 3300,
  49. .EDV1 = 3321,
  50. .EDV2 = 3355,
  51. .EMF = 3679,
  52. .C0 = 430,
  53. .C1 = 0,
  54. .R1 = 408,
  55. .R0 = 334,
  56. .T0 = 4626,
  57. .TC = 11,
  58. .DOD0 = 4044,
  59. .DOD10 = 3905,
  60. .DOD20 = 3807,
  61. .DOD30 = 3718,
  62. .DOD40 = 3642,
  63. .DOD50 = 3585,
  64. .DOD60 = 3546,
  65. .DOD70 = 3514,
  66. .DOD80 = 3477,
  67. .DOD90 = 3411,
  68. .DOD100 = 3299,
  69. };
  70. void furi_hal_power_init() {
  71. LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
  72. LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
  73. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  74. bq27220_init(&furi_hal_i2c_handle_power, &cedv);
  75. bq25896_init(&furi_hal_i2c_handle_power);
  76. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  77. #ifdef FURI_HAL_OS_DEBUG
  78. furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull);
  79. furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull);
  80. #endif
  81. FURI_LOG_I(TAG, "Init OK");
  82. }
  83. bool furi_hal_power_gauge_is_ok() {
  84. bool ret = true;
  85. BatteryStatus battery_status;
  86. OperationStatus operation_status;
  87. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  88. if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR ||
  89. bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) ==
  90. BQ27220_ERROR) {
  91. ret = false;
  92. } else {
  93. ret &= battery_status.BATTPRES;
  94. ret &= operation_status.INITCOMP;
  95. ret &= (cedv.design_cap == bq27220_get_design_capacity(&furi_hal_i2c_handle_power));
  96. }
  97. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  98. return ret;
  99. }
  100. uint16_t furi_hal_power_insomnia_level() {
  101. return furi_hal_power.insomnia;
  102. }
  103. void furi_hal_power_insomnia_enter() {
  104. FURI_CRITICAL_ENTER();
  105. furi_assert(furi_hal_power.insomnia < UINT8_MAX);
  106. furi_hal_power.insomnia++;
  107. FURI_CRITICAL_EXIT();
  108. }
  109. void furi_hal_power_insomnia_exit() {
  110. FURI_CRITICAL_ENTER();
  111. furi_assert(furi_hal_power.insomnia > 0);
  112. furi_hal_power.insomnia--;
  113. FURI_CRITICAL_EXIT();
  114. }
  115. bool furi_hal_power_sleep_available() {
  116. return furi_hal_power.insomnia == 0;
  117. }
  118. bool furi_hal_power_deep_sleep_available() {
  119. return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0;
  120. }
  121. void furi_hal_power_light_sleep() {
  122. __WFI();
  123. }
  124. static inline void furi_hal_power_suspend_aux_periphs() {
  125. // Disable USART
  126. furi_hal_uart_suspend(FuriHalUartIdUSART1);
  127. furi_hal_uart_suspend(FuriHalUartIdLPUART1);
  128. // TODO: Disable USB
  129. }
  130. static inline void furi_hal_power_resume_aux_periphs() {
  131. // Re-enable USART
  132. furi_hal_uart_resume(FuriHalUartIdUSART1);
  133. furi_hal_uart_resume(FuriHalUartIdLPUART1);
  134. // TODO: Re-enable USB
  135. }
  136. void furi_hal_power_deep_sleep() {
  137. furi_hal_power_suspend_aux_periphs();
  138. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  139. ;
  140. if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
  141. if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) {
  142. // Release ENTRY_STOP_MODE semaphore
  143. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  144. // The switch on HSI before entering Stop Mode is required
  145. furi_hal_clock_switch_to_hsi();
  146. }
  147. } else {
  148. /**
  149. * The switch on HSI before entering Stop Mode is required
  150. */
  151. furi_hal_clock_switch_to_hsi();
  152. }
  153. /* Release RCC semaphore */
  154. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  155. // Prepare deep sleep
  156. LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
  157. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
  158. LL_LPM_EnableDeepSleep();
  159. #if defined(__CC_ARM)
  160. // Force store operations
  161. __force_stores();
  162. #endif
  163. __WFI();
  164. LL_LPM_EnableSleep();
  165. // Make sure that values differ to prevent disaster on wfi
  166. LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0);
  167. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  168. LL_PWR_ClearFlag_C1STOP_C1STB();
  169. LL_PWR_ClearFlag_C2STOP_C2STB();
  170. /* Release ENTRY_STOP_MODE semaphore */
  171. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  172. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  173. ;
  174. if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {
  175. furi_hal_clock_switch_to_pll();
  176. }
  177. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  178. furi_hal_power_resume_aux_periphs();
  179. }
  180. void furi_hal_power_sleep() {
  181. if(furi_hal_power_deep_sleep_available()) {
  182. #ifdef FURI_HAL_OS_DEBUG
  183. furi_hal_gpio_write(&gpio_ext_pc3, 1);
  184. #endif
  185. furi_hal_power_deep_sleep();
  186. #ifdef FURI_HAL_OS_DEBUG
  187. furi_hal_gpio_write(&gpio_ext_pc3, 0);
  188. #endif
  189. } else {
  190. #ifdef FURI_HAL_OS_DEBUG
  191. furi_hal_gpio_write(&gpio_ext_pb2, 1);
  192. #endif
  193. furi_hal_power_light_sleep();
  194. #ifdef FURI_HAL_OS_DEBUG
  195. furi_hal_gpio_write(&gpio_ext_pb2, 0);
  196. #endif
  197. }
  198. }
  199. uint8_t furi_hal_power_get_pct() {
  200. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  201. uint8_t ret = bq27220_get_state_of_charge(&furi_hal_i2c_handle_power);
  202. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  203. return ret;
  204. }
  205. uint8_t furi_hal_power_get_bat_health_pct() {
  206. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  207. uint8_t ret = bq27220_get_state_of_health(&furi_hal_i2c_handle_power);
  208. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  209. return ret;
  210. }
  211. bool furi_hal_power_is_charging() {
  212. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  213. bool ret = bq25896_is_charging(&furi_hal_i2c_handle_power);
  214. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  215. return ret;
  216. }
  217. bool furi_hal_power_is_charging_done() {
  218. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  219. bool ret = bq25896_is_charging_done(&furi_hal_i2c_handle_power);
  220. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  221. return ret;
  222. }
  223. void furi_hal_power_shutdown() {
  224. furi_hal_power_insomnia_enter();
  225. furi_hal_bt_reinit();
  226. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  227. ;
  228. if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
  229. if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) {
  230. // Release ENTRY_STOP_MODE semaphore
  231. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  232. }
  233. }
  234. // Prepare Wakeup pin
  235. LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN2);
  236. LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
  237. LL_C2_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
  238. /* Release RCC semaphore */
  239. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  240. LL_PWR_DisableBootC2();
  241. LL_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  242. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  243. LL_LPM_EnableDeepSleep();
  244. __WFI();
  245. furi_crash("Insomniac core2");
  246. }
  247. void furi_hal_power_off() {
  248. // Crutch: shutting down with ext 3V3 off is causing LSE to stop
  249. furi_hal_power_enable_external_3_3v();
  250. furi_delay_us(1000);
  251. // Send poweroff to charger
  252. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  253. bq25896_poweroff(&furi_hal_i2c_handle_power);
  254. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  255. }
  256. void furi_hal_power_reset() {
  257. NVIC_SystemReset();
  258. }
  259. void furi_hal_power_enable_otg() {
  260. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  261. bq25896_enable_otg(&furi_hal_i2c_handle_power);
  262. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  263. }
  264. void furi_hal_power_disable_otg() {
  265. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  266. bq25896_disable_otg(&furi_hal_i2c_handle_power);
  267. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  268. }
  269. bool furi_hal_power_is_otg_enabled() {
  270. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  271. bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power);
  272. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  273. return ret;
  274. }
  275. void furi_hal_power_check_otg_status() {
  276. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  277. if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power))
  278. bq25896_disable_otg(&furi_hal_i2c_handle_power);
  279. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  280. }
  281. uint32_t furi_hal_power_get_battery_remaining_capacity() {
  282. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  283. uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power);
  284. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  285. return ret;
  286. }
  287. uint32_t furi_hal_power_get_battery_full_capacity() {
  288. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  289. uint32_t ret = bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power);
  290. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  291. return ret;
  292. }
  293. uint32_t furi_hal_power_get_battery_design_capacity() {
  294. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  295. uint32_t ret = bq27220_get_design_capacity(&furi_hal_i2c_handle_power);
  296. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  297. return ret;
  298. }
  299. float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) {
  300. float ret = 0.0f;
  301. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  302. if(ic == FuriHalPowerICCharger) {
  303. ret = (float)bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  304. } else if(ic == FuriHalPowerICFuelGauge) {
  305. ret = (float)bq27220_get_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  306. }
  307. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  308. return ret;
  309. }
  310. float furi_hal_power_get_battery_current(FuriHalPowerIC ic) {
  311. float ret = 0.0f;
  312. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  313. if(ic == FuriHalPowerICCharger) {
  314. ret = (float)bq25896_get_vbat_current(&furi_hal_i2c_handle_power) / 1000.0f;
  315. } else if(ic == FuriHalPowerICFuelGauge) {
  316. ret = (float)bq27220_get_current(&furi_hal_i2c_handle_power) / 1000.0f;
  317. }
  318. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  319. return ret;
  320. }
  321. static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) {
  322. float ret = 0.0f;
  323. if(ic == FuriHalPowerICCharger) {
  324. // Linear approximation, +/- 5 C
  325. ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power) / 1000) / 0.6f;
  326. } else if(ic == FuriHalPowerICFuelGauge) {
  327. ret = ((float)bq27220_get_temperature(&furi_hal_i2c_handle_power) - 2731.0f) / 10.0f;
  328. }
  329. return ret;
  330. }
  331. float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
  332. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  333. float ret = furi_hal_power_get_battery_temperature_internal(ic);
  334. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  335. return ret;
  336. }
  337. float furi_hal_power_get_usb_voltage() {
  338. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  339. float ret = (float)bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  340. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  341. return ret;
  342. }
  343. void furi_hal_power_enable_external_3_3v() {
  344. furi_hal_gpio_write(&periph_power, 1);
  345. }
  346. void furi_hal_power_disable_external_3_3v() {
  347. furi_hal_gpio_write(&periph_power, 0);
  348. }
  349. void furi_hal_power_suppress_charge_enter() {
  350. vTaskSuspendAll();
  351. bool disable_charging = furi_hal_power.suppress_charge == 0;
  352. furi_hal_power.suppress_charge++;
  353. xTaskResumeAll();
  354. if(disable_charging) {
  355. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  356. bq25896_disable_charging(&furi_hal_i2c_handle_power);
  357. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  358. }
  359. }
  360. void furi_hal_power_suppress_charge_exit() {
  361. vTaskSuspendAll();
  362. furi_hal_power.suppress_charge--;
  363. bool enable_charging = furi_hal_power.suppress_charge == 0;
  364. xTaskResumeAll();
  365. if(enable_charging) {
  366. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  367. bq25896_enable_charging(&furi_hal_i2c_handle_power);
  368. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  369. }
  370. }
  371. void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) {
  372. furi_assert(out);
  373. FuriString* value = furi_string_alloc();
  374. FuriString* key = furi_string_alloc();
  375. PropertyValueContext property_context = {
  376. .key = key, .value = value, .out = out, .sep = sep, .last = false, .context = context};
  377. if(sep == '.') {
  378. property_value_out(&property_context, NULL, 2, "format", "major", "2");
  379. property_value_out(&property_context, NULL, 2, "format", "minor", "0");
  380. } else {
  381. property_value_out(&property_context, NULL, 3, "power", "info", "major", "1");
  382. property_value_out(&property_context, NULL, 3, "power", "info", "minor", "0");
  383. }
  384. uint8_t charge = furi_hal_power_get_pct();
  385. property_value_out(&property_context, "%u", 2, "charge", "level", charge);
  386. const char* charge_state;
  387. if(furi_hal_power_is_charging()) {
  388. if(charge < 100) {
  389. charge_state = "charging";
  390. } else {
  391. charge_state = "charged";
  392. }
  393. } else {
  394. charge_state = "discharging";
  395. }
  396. property_value_out(&property_context, NULL, 2, "charge", "state", charge_state);
  397. uint16_t voltage =
  398. (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f);
  399. property_value_out(&property_context, "%u", 2, "battery", "voltage", voltage);
  400. int16_t current =
  401. (int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f);
  402. property_value_out(&property_context, "%d", 2, "battery", "current", current);
  403. int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge);
  404. property_value_out(&property_context, "%d", 2, "battery", "temp", temperature);
  405. property_value_out(
  406. &property_context, "%u", 2, "battery", "health", furi_hal_power_get_bat_health_pct());
  407. property_value_out(
  408. &property_context,
  409. "%lu",
  410. 2,
  411. "capacity",
  412. "remain",
  413. furi_hal_power_get_battery_remaining_capacity());
  414. property_value_out(
  415. &property_context,
  416. "%lu",
  417. 2,
  418. "capacity",
  419. "full",
  420. furi_hal_power_get_battery_full_capacity());
  421. property_context.last = true;
  422. property_value_out(
  423. &property_context,
  424. "%lu",
  425. 2,
  426. "capacity",
  427. "design",
  428. furi_hal_power_get_battery_design_capacity());
  429. furi_string_free(key);
  430. furi_string_free(value);
  431. }
  432. void furi_hal_power_debug_get(PropertyValueCallback out, void* context) {
  433. furi_assert(out);
  434. FuriString* value = furi_string_alloc();
  435. FuriString* key = furi_string_alloc();
  436. PropertyValueContext property_context = {
  437. .key = key, .value = value, .out = out, .sep = '.', .last = false, .context = context};
  438. BatteryStatus battery_status;
  439. OperationStatus operation_status;
  440. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  441. // Power Debug version
  442. property_value_out(&property_context, NULL, 2, "format", "major", "1");
  443. property_value_out(&property_context, NULL, 2, "format", "minor", "0");
  444. property_value_out(
  445. &property_context,
  446. "%d",
  447. 2,
  448. "charger",
  449. "vbus",
  450. bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power));
  451. property_value_out(
  452. &property_context,
  453. "%d",
  454. 2,
  455. "charger",
  456. "vsys",
  457. bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power));
  458. property_value_out(
  459. &property_context,
  460. "%d",
  461. 2,
  462. "charger",
  463. "vbat",
  464. bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power));
  465. property_value_out(
  466. &property_context,
  467. "%d",
  468. 2,
  469. "charger",
  470. "current",
  471. bq25896_get_vbat_current(&furi_hal_i2c_handle_power));
  472. const uint32_t ntc_mpct = bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power);
  473. if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) != BQ27220_ERROR &&
  474. bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) !=
  475. BQ27220_ERROR) {
  476. property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct);
  477. property_value_out(&property_context, "%d", 2, "gauge", "calmd", operation_status.CALMD);
  478. property_value_out(&property_context, "%d", 2, "gauge", "sec", operation_status.SEC);
  479. property_value_out(&property_context, "%d", 2, "gauge", "edv2", operation_status.EDV2);
  480. property_value_out(&property_context, "%d", 2, "gauge", "vdq", operation_status.VDQ);
  481. property_value_out(
  482. &property_context, "%d", 2, "gauge", "initcomp", operation_status.INITCOMP);
  483. property_value_out(&property_context, "%d", 2, "gauge", "smth", operation_status.SMTH);
  484. property_value_out(&property_context, "%d", 2, "gauge", "btpint", operation_status.BTPINT);
  485. property_value_out(
  486. &property_context, "%d", 2, "gauge", "cfgupdate", operation_status.CFGUPDATE);
  487. // Battery status register, part 1
  488. property_value_out(&property_context, "%d", 2, "gauge", "chginh", battery_status.CHGINH);
  489. property_value_out(&property_context, "%d", 2, "gauge", "fc", battery_status.FC);
  490. property_value_out(&property_context, "%d", 2, "gauge", "otd", battery_status.OTD);
  491. property_value_out(&property_context, "%d", 2, "gauge", "otc", battery_status.OTC);
  492. property_value_out(&property_context, "%d", 2, "gauge", "sleep", battery_status.SLEEP);
  493. property_value_out(&property_context, "%d", 2, "gauge", "ocvfail", battery_status.OCVFAIL);
  494. property_value_out(&property_context, "%d", 2, "gauge", "ocvcomp", battery_status.OCVCOMP);
  495. property_value_out(&property_context, "%d", 2, "gauge", "fd", battery_status.FD);
  496. // Battery status register, part 2
  497. property_value_out(&property_context, "%d", 2, "gauge", "dsg", battery_status.DSG);
  498. property_value_out(&property_context, "%d", 2, "gauge", "sysdwn", battery_status.SYSDWN);
  499. property_value_out(&property_context, "%d", 2, "gauge", "tda", battery_status.TDA);
  500. property_value_out(
  501. &property_context, "%d", 2, "gauge", "battpres", battery_status.BATTPRES);
  502. property_value_out(&property_context, "%d", 2, "gauge", "authgd", battery_status.AUTH_GD);
  503. property_value_out(&property_context, "%d", 2, "gauge", "ocvgd", battery_status.OCVGD);
  504. property_value_out(&property_context, "%d", 2, "gauge", "tca", battery_status.TCA);
  505. property_value_out(&property_context, "%d", 2, "gauge", "rsvd", battery_status.RSVD);
  506. // Voltage and current info
  507. property_value_out(
  508. &property_context,
  509. "%d",
  510. 3,
  511. "gauge",
  512. "capacity",
  513. "full",
  514. bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power));
  515. property_value_out(
  516. &property_context,
  517. "%d",
  518. 3,
  519. "gauge",
  520. "capacity",
  521. "design",
  522. bq27220_get_design_capacity(&furi_hal_i2c_handle_power));
  523. property_value_out(
  524. &property_context,
  525. "%d",
  526. 3,
  527. "gauge",
  528. "capacity",
  529. "remain",
  530. bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power));
  531. property_value_out(
  532. &property_context,
  533. "%d",
  534. 3,
  535. "gauge",
  536. "state",
  537. "charge",
  538. bq27220_get_state_of_charge(&furi_hal_i2c_handle_power));
  539. property_value_out(
  540. &property_context,
  541. "%d",
  542. 3,
  543. "gauge",
  544. "state",
  545. "health",
  546. bq27220_get_state_of_health(&furi_hal_i2c_handle_power));
  547. property_value_out(
  548. &property_context,
  549. "%d",
  550. 2,
  551. "gauge",
  552. "voltage",
  553. bq27220_get_voltage(&furi_hal_i2c_handle_power));
  554. property_value_out(
  555. &property_context,
  556. "%d",
  557. 2,
  558. "gauge",
  559. "current",
  560. bq27220_get_current(&furi_hal_i2c_handle_power));
  561. property_context.last = true;
  562. const int battery_temp =
  563. (int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge);
  564. property_value_out(&property_context, "%d", 2, "gauge", "temperature", battery_temp);
  565. } else {
  566. property_context.last = true;
  567. property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct);
  568. }
  569. furi_string_free(key);
  570. furi_string_free(value);
  571. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  572. }