furi_hal_power.c 22 KB

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