furi_hal_power.c 23 KB

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