furi_hal_power.c 22 KB

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