furi-hal-power.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #include <furi-hal-power.h>
  2. #include <furi-hal-clock.h>
  3. #include <furi-hal-bt.h>
  4. #include <stm32wbxx_ll_rcc.h>
  5. #include <stm32wbxx_ll_pwr.h>
  6. #include <stm32wbxx_ll_hsem.h>
  7. #include <stm32wbxx_ll_cortex.h>
  8. #include <stm32wbxx_ll_gpio.h>
  9. #include <main.h>
  10. #include <hw_conf.h>
  11. #include <bq27220.h>
  12. #include <bq25896.h>
  13. #include <furi.h>
  14. typedef struct {
  15. volatile uint8_t insomnia;
  16. volatile uint8_t deep_insomnia;
  17. volatile uint8_t suppress_charge;
  18. } FuriHalPower;
  19. static volatile FuriHalPower furi_hal_power = {
  20. .insomnia = 0,
  21. .deep_insomnia = 1,
  22. .suppress_charge = 0,
  23. };
  24. const ParamCEDV cedv = {
  25. .cedv_conf.gauge_conf = {
  26. .CCT = 1,
  27. .CSYNC = 0,
  28. .EDV_CMP = 0,
  29. .SC = 1,
  30. .FIXED_EDV0 = 1,
  31. .FCC_LIM = 1,
  32. .FC_FOR_VDQ = 1,
  33. .IGNORE_SD = 1,
  34. .SME0 = 0,
  35. },
  36. .full_charge_cap = 2100,
  37. .design_cap = 2100,
  38. .EDV0 = 3300,
  39. .EDV1 = 3321,
  40. .EDV2 = 3355,
  41. .EMF = 3679,
  42. .C0 = 430,
  43. .C1 = 0,
  44. .R1 = 408,
  45. .R0 = 334,
  46. .T0 = 4626,
  47. .TC = 11,
  48. .DOD0 = 4044,
  49. .DOD10 = 3905,
  50. .DOD20 = 3807,
  51. .DOD30 = 3718,
  52. .DOD40 = 3642,
  53. .DOD50 = 3585,
  54. .DOD60 = 3546,
  55. .DOD70 = 3514,
  56. .DOD80 = 3477,
  57. .DOD90 = 3411,
  58. .DOD100 = 3299,
  59. };
  60. void HAL_RCC_CSSCallback(void) {
  61. // TODO: notify user about issue with HSE
  62. furi_hal_power_reset();
  63. }
  64. void furi_hal_power_init() {
  65. LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
  66. LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
  67. bq27220_init(&cedv);
  68. bq25896_init();
  69. FURI_LOG_I("FuriHalPower", "Init OK");
  70. }
  71. uint16_t furi_hal_power_insomnia_level() {
  72. return furi_hal_power.insomnia;
  73. }
  74. void furi_hal_power_insomnia_enter() {
  75. vTaskSuspendAll();
  76. furi_hal_power.insomnia++;
  77. xTaskResumeAll();
  78. }
  79. void furi_hal_power_insomnia_exit() {
  80. vTaskSuspendAll();
  81. furi_hal_power.insomnia--;
  82. xTaskResumeAll();
  83. }
  84. bool furi_hal_power_sleep_available() {
  85. return furi_hal_power.insomnia == 0;
  86. }
  87. bool furi_hal_power_deep_sleep_available() {
  88. return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0;
  89. }
  90. void furi_hal_power_light_sleep() {
  91. __WFI();
  92. }
  93. void furi_hal_power_deep_sleep() {
  94. while( LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID));
  95. if (!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
  96. if(LL_PWR_IsActiveFlag_C2DS()) {
  97. // Release ENTRY_STOP_MODE semaphore
  98. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  99. // The switch on HSI before entering Stop Mode is required
  100. furi_hal_clock_switch_to_hsi();
  101. }
  102. } else {
  103. /**
  104. * The switch on HSI before entering Stop Mode is required
  105. */
  106. furi_hal_clock_switch_to_hsi();
  107. }
  108. /* Release RCC semaphore */
  109. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  110. // Prepare deep sleep
  111. LL_PWR_SetPowerMode(LL_PWR_MODE_STOP1);
  112. LL_LPM_EnableDeepSleep();
  113. #if defined ( __CC_ARM)
  114. // Force store operations
  115. __force_stores();
  116. #endif
  117. __WFI();
  118. /* Release ENTRY_STOP_MODE semaphore */
  119. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  120. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID));
  121. if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {
  122. furi_hal_clock_switch_to_pll();
  123. }
  124. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  125. }
  126. void furi_hal_power_sleep() {
  127. if(furi_hal_power_deep_sleep_available()) {
  128. furi_hal_power_deep_sleep();
  129. } else {
  130. furi_hal_power_light_sleep();
  131. }
  132. }
  133. uint8_t furi_hal_power_get_pct() {
  134. return bq27220_get_state_of_charge();
  135. }
  136. uint8_t furi_hal_power_get_bat_health_pct() {
  137. return bq27220_get_state_of_health();
  138. }
  139. bool furi_hal_power_is_charging() {
  140. return bq25896_is_charging();
  141. }
  142. void furi_hal_power_off() {
  143. bq25896_poweroff();
  144. }
  145. void furi_hal_power_reset() {
  146. NVIC_SystemReset();
  147. }
  148. void furi_hal_power_enable_otg() {
  149. bq25896_enable_otg();
  150. }
  151. void furi_hal_power_disable_otg() {
  152. bq25896_disable_otg();
  153. }
  154. bool furi_hal_power_is_otg_enabled() {
  155. return bq25896_is_otg_enabled();
  156. }
  157. uint32_t furi_hal_power_get_battery_remaining_capacity() {
  158. return bq27220_get_remaining_capacity();
  159. }
  160. uint32_t furi_hal_power_get_battery_full_capacity() {
  161. return bq27220_get_full_charge_capacity();
  162. }
  163. float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) {
  164. if (ic == FuriHalPowerICCharger) {
  165. return (float)bq25896_get_vbat_voltage() / 1000.0f;
  166. } else if (ic == FuriHalPowerICFuelGauge) {
  167. return (float)bq27220_get_voltage() / 1000.0f;
  168. } else {
  169. return 0.0f;
  170. }
  171. }
  172. float furi_hal_power_get_battery_current(FuriHalPowerIC ic) {
  173. if (ic == FuriHalPowerICCharger) {
  174. return (float)bq25896_get_vbat_current() / 1000.0f;
  175. } else if (ic == FuriHalPowerICFuelGauge) {
  176. return (float)bq27220_get_current() / 1000.0f;
  177. } else {
  178. return 0.0f;
  179. }
  180. }
  181. float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
  182. if (ic == FuriHalPowerICCharger) {
  183. // Linear approximation, +/- 5 C
  184. return (71.0f - (float)bq25896_get_ntc_mpct()/1000) / 0.6f;
  185. } else if (ic == FuriHalPowerICFuelGauge) {
  186. return ((float)bq27220_get_temperature() - 2731.0f) / 10.0f;
  187. } else {
  188. return 0.0f;
  189. }
  190. }
  191. float furi_hal_power_get_usb_voltage(){
  192. return (float)bq25896_get_vbus_voltage() / 1000.0f;
  193. }
  194. void furi_hal_power_dump_state() {
  195. BatteryStatus battery_status;
  196. OperationStatus operation_status;
  197. if (bq27220_get_battery_status(&battery_status) == BQ27220_ERROR
  198. || bq27220_get_operation_status(&operation_status) == BQ27220_ERROR) {
  199. printf("Failed to get bq27220 status. Communication error.\r\n");
  200. } else {
  201. printf(
  202. "bq27220: CALMD: %d, SEC0: %d, SEC1: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n",
  203. operation_status.CALMD, operation_status.SEC0, operation_status.SEC1,
  204. operation_status.EDV2, operation_status.VDQ, operation_status.INITCOMP,
  205. operation_status.SMTH, operation_status.BTPINT, operation_status.CFGUPDATE
  206. );
  207. // Battery status register, part 1
  208. printf(
  209. "bq27220: CHGINH: %d, FC: %d, OTD: %d, OTC: %d, SLEEP: %d, OCVFAIL: %d, OCVCOMP: %d, FD: %d\r\n",
  210. battery_status.CHGINH, battery_status.FC, battery_status.OTD,
  211. battery_status.OTC, battery_status.SLEEP, battery_status.OCVFAIL,
  212. battery_status.OCVCOMP, battery_status.FD
  213. );
  214. // Battery status register, part 2
  215. printf(
  216. "bq27220: DSG: %d, SYSDWN: %d, TDA: %d, BATTPRES: %d, AUTH_GD: %d, OCVGD: %d, TCA: %d, RSVD: %d\r\n",
  217. battery_status.DSG, battery_status.SYSDWN, battery_status.TDA,
  218. battery_status.BATTPRES, battery_status.AUTH_GD, battery_status.OCVGD,
  219. battery_status.TCA, battery_status.RSVD
  220. );
  221. // Voltage and current info
  222. printf(
  223. "bq27220: Full capacity: %dmAh, Design capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%, State of health: %d%%\r\n",
  224. bq27220_get_full_charge_capacity(), bq27220_get_design_capacity(), bq27220_get_remaining_capacity(),
  225. bq27220_get_state_of_charge(), bq27220_get_state_of_health()
  226. );
  227. printf(
  228. "bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n",
  229. bq27220_get_voltage(), bq27220_get_current(), (int)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge)
  230. );
  231. }
  232. printf(
  233. "bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %ldm%%\r\n",
  234. bq25896_get_vbus_voltage(), bq25896_get_vsys_voltage(),
  235. bq25896_get_vbat_voltage(), bq25896_get_vbat_current(),
  236. bq25896_get_ntc_mpct()
  237. );
  238. }
  239. void furi_hal_power_enable_external_3_3v(){
  240. LL_GPIO_SetOutputPin(PERIPH_POWER_GPIO_Port, PERIPH_POWER_Pin);
  241. }
  242. void furi_hal_power_disable_external_3_3v(){
  243. LL_GPIO_ResetOutputPin(PERIPH_POWER_GPIO_Port, PERIPH_POWER_Pin);
  244. }
  245. void furi_hal_power_suppress_charge_enter() {
  246. vTaskSuspendAll();
  247. bool disable_charging = furi_hal_power.suppress_charge == 0;
  248. furi_hal_power.suppress_charge++;
  249. xTaskResumeAll();
  250. if (disable_charging) {
  251. bq25896_disable_charging();
  252. }
  253. }
  254. void furi_hal_power_suppress_charge_exit() {
  255. vTaskSuspendAll();
  256. furi_hal_power.suppress_charge--;
  257. bool enable_charging = furi_hal_power.suppress_charge == 0;
  258. xTaskResumeAll();
  259. if (enable_charging) {
  260. bq25896_enable_charging();
  261. }
  262. }