furi_hal_power.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  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 <stm32wbxx_ll_rcc.h>
  8. #include <stm32wbxx_ll_pwr.h>
  9. #include <stm32wbxx_ll_hsem.h>
  10. #include <stm32wbxx_ll_cortex.h>
  11. #include <stm32wbxx_ll_gpio.h>
  12. #include <hw_conf.h>
  13. #include <bq27220.h>
  14. #include <bq25896.h>
  15. #include <furi.h>
  16. #define TAG "FuriHalPower"
  17. #ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED
  18. #define FURI_HAL_POWER_DEEP_INSOMNIA 0
  19. #else
  20. #define FURI_HAL_POWER_DEEP_INSOMNIA 1
  21. #endif
  22. typedef struct {
  23. volatile uint8_t insomnia;
  24. volatile uint8_t deep_insomnia;
  25. volatile uint8_t suppress_charge;
  26. uint8_t gauge_initialized;
  27. uint8_t charger_initialized;
  28. } FuriHalPower;
  29. static volatile FuriHalPower furi_hal_power = {
  30. .insomnia = 0,
  31. .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA,
  32. .suppress_charge = 0,
  33. };
  34. const ParamCEDV cedv = {
  35. .cedv_conf.gauge_conf =
  36. {
  37. .CCT = 1,
  38. .CSYNC = 0,
  39. .EDV_CMP = 0,
  40. .SC = 1,
  41. .FIXED_EDV0 = 1,
  42. .FCC_LIM = 1,
  43. .FC_FOR_VDQ = 1,
  44. .IGNORE_SD = 1,
  45. .SME0 = 0,
  46. },
  47. .full_charge_cap = 2101,
  48. .design_cap = 2101,
  49. .EDV0 = 3300,
  50. .EDV1 = 3321,
  51. .EDV2 = 3355,
  52. .EMF = 3679,
  53. .C0 = 430,
  54. .C1 = 0,
  55. .R1 = 408,
  56. .R0 = 334,
  57. .T0 = 4626,
  58. .TC = 11,
  59. .DOD0 = 4044,
  60. .DOD10 = 3905,
  61. .DOD20 = 3807,
  62. .DOD30 = 3718,
  63. .DOD40 = 3642,
  64. .DOD50 = 3585,
  65. .DOD60 = 3546,
  66. .DOD70 = 3514,
  67. .DOD80 = 3477,
  68. .DOD90 = 3411,
  69. .DOD100 = 3299,
  70. };
  71. void furi_hal_power_init() {
  72. LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
  73. LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
  74. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  75. bq27220_init(&furi_hal_i2c_handle_power, &cedv);
  76. bq25896_init(&furi_hal_i2c_handle_power);
  77. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  78. #ifdef FURI_HAL_OS_DEBUG
  79. furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull);
  80. furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull);
  81. #endif
  82. FURI_LOG_I(TAG, "Init OK");
  83. }
  84. bool furi_hal_power_gauge_is_ok() {
  85. bool ret = true;
  86. BatteryStatus battery_status;
  87. OperationStatus operation_status;
  88. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  89. if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR ||
  90. bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) ==
  91. BQ27220_ERROR) {
  92. ret = false;
  93. } else {
  94. ret &= battery_status.BATTPRES;
  95. ret &= operation_status.INITCOMP;
  96. ret &= (cedv.design_cap == bq27220_get_design_capacity(&furi_hal_i2c_handle_power));
  97. }
  98. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  99. return ret;
  100. }
  101. uint16_t furi_hal_power_insomnia_level() {
  102. return furi_hal_power.insomnia;
  103. }
  104. void furi_hal_power_insomnia_enter() {
  105. FURI_CRITICAL_ENTER();
  106. furi_assert(furi_hal_power.insomnia < UINT8_MAX);
  107. furi_hal_power.insomnia++;
  108. FURI_CRITICAL_EXIT();
  109. }
  110. void furi_hal_power_insomnia_exit() {
  111. FURI_CRITICAL_ENTER();
  112. furi_assert(furi_hal_power.insomnia > 0);
  113. furi_hal_power.insomnia--;
  114. FURI_CRITICAL_EXIT();
  115. }
  116. bool furi_hal_power_sleep_available() {
  117. return furi_hal_power.insomnia == 0;
  118. }
  119. bool furi_hal_power_deep_sleep_available() {
  120. return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0;
  121. }
  122. void furi_hal_power_light_sleep() {
  123. __WFI();
  124. }
  125. static inline void furi_hal_power_suspend_aux_periphs() {
  126. // Disable USART
  127. furi_hal_uart_suspend(FuriHalUartIdUSART1);
  128. furi_hal_uart_suspend(FuriHalUartIdLPUART1);
  129. // TODO: Disable USB
  130. }
  131. static inline void furi_hal_power_resume_aux_periphs() {
  132. // Re-enable USART
  133. furi_hal_uart_resume(FuriHalUartIdUSART1);
  134. furi_hal_uart_resume(FuriHalUartIdLPUART1);
  135. // TODO: Re-enable USB
  136. }
  137. void furi_hal_power_deep_sleep() {
  138. furi_hal_power_suspend_aux_periphs();
  139. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  140. ;
  141. if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
  142. if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) {
  143. // Release ENTRY_STOP_MODE semaphore
  144. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  145. // The switch on HSI before entering Stop Mode is required
  146. furi_hal_clock_switch_to_hsi();
  147. }
  148. } else {
  149. /**
  150. * The switch on HSI before entering Stop Mode is required
  151. */
  152. furi_hal_clock_switch_to_hsi();
  153. }
  154. /* Release RCC semaphore */
  155. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  156. // Prepare deep sleep
  157. LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
  158. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
  159. LL_LPM_EnableDeepSleep();
  160. #if defined(__CC_ARM)
  161. // Force store operations
  162. __force_stores();
  163. #endif
  164. __WFI();
  165. LL_LPM_EnableSleep();
  166. // Make sure that values differ to prevent disaster on wfi
  167. LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0);
  168. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  169. LL_PWR_ClearFlag_C1STOP_C1STB();
  170. LL_PWR_ClearFlag_C2STOP_C2STB();
  171. /* Release ENTRY_STOP_MODE semaphore */
  172. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  173. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  174. ;
  175. if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {
  176. furi_hal_clock_switch_to_pll();
  177. }
  178. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  179. furi_hal_power_resume_aux_periphs();
  180. }
  181. void furi_hal_power_sleep() {
  182. if(furi_hal_power_deep_sleep_available()) {
  183. #ifdef FURI_HAL_OS_DEBUG
  184. furi_hal_gpio_write(&gpio_ext_pc3, 1);
  185. #endif
  186. furi_hal_power_deep_sleep();
  187. #ifdef FURI_HAL_OS_DEBUG
  188. furi_hal_gpio_write(&gpio_ext_pc3, 0);
  189. #endif
  190. } else {
  191. #ifdef FURI_HAL_OS_DEBUG
  192. furi_hal_gpio_write(&gpio_ext_pb2, 1);
  193. #endif
  194. furi_hal_power_light_sleep();
  195. #ifdef FURI_HAL_OS_DEBUG
  196. furi_hal_gpio_write(&gpio_ext_pb2, 0);
  197. #endif
  198. }
  199. }
  200. uint8_t furi_hal_power_get_pct() {
  201. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  202. uint8_t ret = bq27220_get_state_of_charge(&furi_hal_i2c_handle_power);
  203. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  204. return ret;
  205. }
  206. uint8_t furi_hal_power_get_bat_health_pct() {
  207. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  208. uint8_t ret = bq27220_get_state_of_health(&furi_hal_i2c_handle_power);
  209. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  210. return ret;
  211. }
  212. bool furi_hal_power_is_charging() {
  213. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  214. bool ret = bq25896_is_charging(&furi_hal_i2c_handle_power);
  215. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  216. return ret;
  217. }
  218. bool furi_hal_power_is_charging_done() {
  219. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  220. bool ret = bq25896_is_charging_done(&furi_hal_i2c_handle_power);
  221. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  222. return ret;
  223. }
  224. void furi_hal_power_shutdown() {
  225. furi_hal_power_insomnia_enter();
  226. furi_hal_bt_reinit();
  227. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  228. ;
  229. if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
  230. if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) {
  231. // Release ENTRY_STOP_MODE semaphore
  232. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  233. }
  234. }
  235. // Prepare Wakeup pin
  236. LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN2);
  237. LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
  238. LL_C2_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
  239. /* Release RCC semaphore */
  240. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  241. LL_PWR_DisableBootC2();
  242. LL_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  243. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  244. LL_LPM_EnableDeepSleep();
  245. __WFI();
  246. furi_crash("Insomniac core2");
  247. }
  248. void furi_hal_power_off() {
  249. // Crutch: shutting down with ext 3V3 off is causing LSE to stop
  250. furi_hal_power_enable_external_3_3v();
  251. furi_hal_vibro_on(true);
  252. furi_delay_us(50000);
  253. // Send poweroff to charger
  254. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  255. bq25896_poweroff(&furi_hal_i2c_handle_power);
  256. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  257. furi_hal_vibro_on(false);
  258. }
  259. void furi_hal_power_reset() {
  260. NVIC_SystemReset();
  261. }
  262. void furi_hal_power_enable_otg() {
  263. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  264. bq25896_enable_otg(&furi_hal_i2c_handle_power);
  265. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  266. }
  267. void furi_hal_power_disable_otg() {
  268. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  269. bq25896_disable_otg(&furi_hal_i2c_handle_power);
  270. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  271. }
  272. bool furi_hal_power_is_otg_enabled() {
  273. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  274. bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power);
  275. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  276. return ret;
  277. }
  278. float furi_hal_power_get_battery_charging_voltage() {
  279. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  280. float ret = (float)bq25896_get_vreg_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  281. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  282. return ret;
  283. }
  284. void furi_hal_power_set_battery_charging_voltage(float voltage) {
  285. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  286. // Adding 0.0005 is necessary because 4.016f is 4.015999794000, which gets truncated
  287. bq25896_set_vreg_voltage(&furi_hal_i2c_handle_power, (uint16_t)(voltage * 1000.0f + 0.0005f));
  288. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  289. }
  290. void furi_hal_power_check_otg_status() {
  291. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  292. if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power))
  293. bq25896_disable_otg(&furi_hal_i2c_handle_power);
  294. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  295. }
  296. uint32_t furi_hal_power_get_battery_remaining_capacity() {
  297. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  298. uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power);
  299. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  300. return ret;
  301. }
  302. uint32_t furi_hal_power_get_battery_full_capacity() {
  303. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  304. uint32_t ret = bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power);
  305. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  306. return ret;
  307. }
  308. uint32_t furi_hal_power_get_battery_design_capacity() {
  309. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  310. uint32_t ret = bq27220_get_design_capacity(&furi_hal_i2c_handle_power);
  311. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  312. return ret;
  313. }
  314. float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) {
  315. float ret = 0.0f;
  316. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  317. if(ic == FuriHalPowerICCharger) {
  318. ret = (float)bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  319. } else if(ic == FuriHalPowerICFuelGauge) {
  320. ret = (float)bq27220_get_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  321. }
  322. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  323. return ret;
  324. }
  325. float furi_hal_power_get_battery_current(FuriHalPowerIC ic) {
  326. float ret = 0.0f;
  327. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  328. if(ic == FuriHalPowerICCharger) {
  329. ret = (float)bq25896_get_vbat_current(&furi_hal_i2c_handle_power) / 1000.0f;
  330. } else if(ic == FuriHalPowerICFuelGauge) {
  331. ret = (float)bq27220_get_current(&furi_hal_i2c_handle_power) / 1000.0f;
  332. }
  333. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  334. return ret;
  335. }
  336. static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) {
  337. float ret = 0.0f;
  338. if(ic == FuriHalPowerICCharger) {
  339. // Linear approximation, +/- 5 C
  340. ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power) / 1000) / 0.6f;
  341. } else if(ic == FuriHalPowerICFuelGauge) {
  342. ret = ((float)bq27220_get_temperature(&furi_hal_i2c_handle_power) - 2731.0f) / 10.0f;
  343. }
  344. return ret;
  345. }
  346. float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
  347. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  348. float ret = furi_hal_power_get_battery_temperature_internal(ic);
  349. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  350. return ret;
  351. }
  352. float furi_hal_power_get_usb_voltage() {
  353. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  354. float ret = (float)bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  355. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  356. return ret;
  357. }
  358. void furi_hal_power_enable_external_3_3v() {
  359. furi_hal_gpio_write(&periph_power, 1);
  360. }
  361. void furi_hal_power_disable_external_3_3v() {
  362. furi_hal_gpio_write(&periph_power, 0);
  363. }
  364. void furi_hal_power_suppress_charge_enter() {
  365. vTaskSuspendAll();
  366. bool disable_charging = furi_hal_power.suppress_charge == 0;
  367. furi_hal_power.suppress_charge++;
  368. xTaskResumeAll();
  369. if(disable_charging) {
  370. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  371. bq25896_disable_charging(&furi_hal_i2c_handle_power);
  372. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  373. }
  374. }
  375. void furi_hal_power_suppress_charge_exit() {
  376. vTaskSuspendAll();
  377. furi_hal_power.suppress_charge--;
  378. bool enable_charging = furi_hal_power.suppress_charge == 0;
  379. xTaskResumeAll();
  380. if(enable_charging) {
  381. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  382. bq25896_enable_charging(&furi_hal_i2c_handle_power);
  383. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  384. }
  385. }
  386. void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) {
  387. furi_assert(out);
  388. FuriString* value = furi_string_alloc();
  389. FuriString* key = furi_string_alloc();
  390. PropertyValueContext property_context = {
  391. .key = key, .value = value, .out = out, .sep = sep, .last = false, .context = context};
  392. if(sep == '.') {
  393. property_value_out(&property_context, NULL, 2, "format", "major", "2");
  394. property_value_out(&property_context, NULL, 2, "format", "minor", "1");
  395. } else {
  396. property_value_out(&property_context, NULL, 3, "power", "info", "major", "1");
  397. property_value_out(&property_context, NULL, 3, "power", "info", "minor", "1");
  398. }
  399. uint8_t charge = furi_hal_power_get_pct();
  400. property_value_out(&property_context, "%u", 2, "charge", "level", charge);
  401. const char* charge_state;
  402. if(furi_hal_power_is_charging()) {
  403. if((charge < 100) && (!furi_hal_power_is_charging_done())) {
  404. charge_state = "charging";
  405. } else {
  406. charge_state = "charged";
  407. }
  408. } else {
  409. charge_state = "discharging";
  410. }
  411. property_value_out(&property_context, NULL, 2, "charge", "state", charge_state);
  412. uint16_t charge_voltage = (uint16_t)(furi_hal_power_get_battery_charging_voltage() * 1000.f);
  413. property_value_out(&property_context, "%u", 2, "charge", "voltage", charge_voltage);
  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. }