emu-power.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2023-02-25 GuEe-GUI the first version
  9. */
  10. #include <rtthread.h>
  11. #include <rtdevice.h>
  12. #include <drivers/misc.h>
  13. #define POLL_INTERVAL_MS 3000
  14. #define CHARGE_STEP_FAST 4
  15. #define CHARGE_STEP_NORMAL 2
  16. #define CHARGE_STEP_TRICKLE 1
  17. #define DISCHARGE_BASE_RATE 1
  18. #define TEMP_SENSITIVITY 30
  19. struct emu_power
  20. {
  21. struct rt_device parent;
  22. struct rt_power_supply battery;
  23. struct rt_power_supply charger;
  24. struct rt_timer poller;
  25. rt_uint32_t status;
  26. rt_uint32_t health;
  27. rt_uint32_t present;
  28. rt_uint32_t capacity;
  29. rt_uint32_t voltage;
  30. rt_uint32_t temp;
  31. rt_uint32_t charge_counter;
  32. rt_uint32_t current_now;
  33. rt_uint32_t current_avg;
  34. rt_uint32_t charge_full_uah;
  35. rt_uint32_t cycle_count;
  36. rt_uint32_t ac_online;
  37. rt_uint32_t voltage_max;
  38. rt_uint32_t current_max;
  39. rt_tick_t last_poll_tick;
  40. rt_uint8_t load_index;
  41. rt_ubase_t cpu_load;
  42. rt_ubase_t load_history[5];
  43. rt_ubase_t last_idle;
  44. rt_ubase_t last_total;
  45. };
  46. static struct emu_power _emu_power;
  47. static enum rt_power_supply_property emu_battery_properties[] =
  48. {
  49. RT_POWER_SUPPLY_PROP_STATUS,
  50. RT_POWER_SUPPLY_PROP_HEALTH,
  51. RT_POWER_SUPPLY_PROP_PRESENT,
  52. RT_POWER_SUPPLY_PROP_TECHNOLOGY,
  53. RT_POWER_SUPPLY_PROP_CAPACITY,
  54. RT_POWER_SUPPLY_PROP_VOLTAGE_NOW,
  55. RT_POWER_SUPPLY_PROP_TEMP,
  56. RT_POWER_SUPPLY_PROP_CHARGE_COUNTER,
  57. RT_POWER_SUPPLY_PROP_CURRENT_NOW,
  58. RT_POWER_SUPPLY_PROP_CURRENT_AVG,
  59. RT_POWER_SUPPLY_PROP_CHARGE_FULL,
  60. RT_POWER_SUPPLY_PROP_CYCLE_COUNT,
  61. RT_POWER_SUPPLY_PROP_SCOPE,
  62. };
  63. static struct rt_power_supply_battery_info emu_battery_info =
  64. {
  65. .technology = RT_POWER_SUPPLY_TECHNOLOGY_LION,
  66. .energy_full_design_uwh = 3000000000, /* 3000mWh */
  67. .charge_full_design_uah = 3000000, /* 3000mAh */
  68. .voltage_min_design_uv = 3000000, /* 3.0V */
  69. .voltage_max_design_uv = 4200000, /* 4.2V */
  70. .precharge_current_ua = 500000, /* 500mA */
  71. .charge_term_current_ua = 1000000, /* 1000mA */
  72. .charge_restart_voltage_uv = 3500000, /* 3.5V */
  73. .constant_charge_current_max_ua = 2000000, /* 2000mA */
  74. .constant_charge_voltage_max_uv = 4200000, /* 4.2V */
  75. .temp_ambient_alert_min = -10000, /* -10C */
  76. .temp_ambient_alert_max = 40000, /* 40C */
  77. .temp_alert_min = 20000, /* 20C */
  78. .temp_alert_max = 25000, /* 25C */
  79. .temp_min = 0, /* 0C */
  80. .temp_max = 35000, /* 35C */
  81. };
  82. static rt_err_t emu_battery_get_property(struct rt_power_supply *psy,
  83. enum rt_power_supply_property prop, union rt_power_supply_property_val *val)
  84. {
  85. struct emu_power *ep = rt_container_of(psy, struct emu_power, battery);
  86. switch (prop)
  87. {
  88. case RT_POWER_SUPPLY_PROP_STATUS:
  89. val->intval = ep->status;
  90. break;
  91. case RT_POWER_SUPPLY_PROP_HEALTH:
  92. val->intval = ep->health;
  93. break;
  94. case RT_POWER_SUPPLY_PROP_PRESENT:
  95. val->intval = ep->present;
  96. break;
  97. case RT_POWER_SUPPLY_PROP_TECHNOLOGY:
  98. val->intval = RT_POWER_SUPPLY_TECHNOLOGY_LION;
  99. break;
  100. case RT_POWER_SUPPLY_PROP_CAPACITY:
  101. val->intval = ep->capacity;
  102. break;
  103. case RT_POWER_SUPPLY_PROP_VOLTAGE_NOW:
  104. val->intval = ep->voltage;
  105. break;
  106. case RT_POWER_SUPPLY_PROP_TEMP:
  107. val->intval = ep->temp;
  108. break;
  109. case RT_POWER_SUPPLY_PROP_CHARGE_COUNTER:
  110. val->intval = ep->charge_counter;
  111. break;
  112. case RT_POWER_SUPPLY_PROP_CURRENT_NOW:
  113. val->intval = ep->current_now;
  114. break;
  115. case RT_POWER_SUPPLY_PROP_CURRENT_AVG:
  116. val->intval = ep->current_avg;
  117. break;
  118. case RT_POWER_SUPPLY_PROP_CHARGE_FULL:
  119. val->intval = ep->charge_full_uah;
  120. break;
  121. case RT_POWER_SUPPLY_PROP_CYCLE_COUNT:
  122. val->intval = ep->cycle_count;
  123. break;
  124. case RT_POWER_SUPPLY_PROP_SCOPE:
  125. val->intval = RT_POWER_SUPPLY_SCOPE_SYSTEM;
  126. break;
  127. default:
  128. return -RT_EINVAL;
  129. }
  130. return RT_EOK;
  131. }
  132. static const struct rt_power_supply_ops emu_battery_ops =
  133. {
  134. .get_property = emu_battery_get_property,
  135. };
  136. static enum rt_power_supply_property emu_charger_properties[] =
  137. {
  138. RT_POWER_SUPPLY_PROP_ONLINE,
  139. RT_POWER_SUPPLY_PROP_VOLTAGE_MAX,
  140. RT_POWER_SUPPLY_PROP_CURRENT_MAX,
  141. };
  142. static rt_err_t emu_charger_get_property(struct rt_power_supply *psy,
  143. enum rt_power_supply_property prop, union rt_power_supply_property_val *val)
  144. {
  145. struct emu_power *ep = rt_container_of(psy, struct emu_power, charger);
  146. switch (prop)
  147. {
  148. case RT_POWER_SUPPLY_PROP_ONLINE:
  149. val->intval = ep->ac_online;
  150. break;
  151. case RT_POWER_SUPPLY_PROP_VOLTAGE_MAX:
  152. val->intval = ep->voltage_max;
  153. break;
  154. case RT_POWER_SUPPLY_PROP_CURRENT_MAX:
  155. val->intval = ep->current_max;
  156. break;
  157. default:
  158. return -RT_ENOSYS;
  159. }
  160. return RT_EOK;
  161. }
  162. static const struct rt_power_supply_ops emu_charger_ops =
  163. {
  164. .get_property = emu_charger_get_property,
  165. };
  166. static void emu_power_poll(void *param)
  167. {
  168. rt_tick_t current_tick;
  169. rt_uint32_t elapsed_ms, avg_load = 0;
  170. rt_ubase_t current_idle, current_total, idle_diff, total_diff;
  171. struct rt_cpu_usage_stats *stats = &rt_cpu_self()->cpu_stat;
  172. struct emu_power *ep = param;
  173. current_tick = rt_tick_get();
  174. elapsed_ms = (current_tick - ep->last_poll_tick) * (1000 / RT_TICK_PER_SECOND);
  175. ep->last_poll_tick = current_tick;
  176. current_idle = stats->idle;
  177. current_total = stats->user + stats->system + stats->irq + current_idle;
  178. idle_diff = current_idle - ep->last_idle;
  179. total_diff = current_total - ep->last_total;
  180. ep->last_idle = current_idle;
  181. ep->last_total = current_total;
  182. if (total_diff > 0)
  183. {
  184. ep->cpu_load = 100 - (idle_diff * 100) / total_diff;
  185. }
  186. else
  187. {
  188. ep->cpu_load = 0;
  189. }
  190. ep->cpu_load = rt_clamp((rt_ubase_t)ep->cpu_load, 0UL, 100UL);
  191. ep->load_history[ep->load_index++ % RT_ARRAY_SIZE(ep->load_history)] = ep->cpu_load;
  192. for (int i = 0; i < RT_ARRAY_SIZE(ep->load_history); i++)
  193. {
  194. avg_load += ep->load_history[i];
  195. }
  196. avg_load /= RT_ARRAY_SIZE(ep->load_history);
  197. if (ep->ac_online)
  198. {
  199. int step;
  200. if (ep->capacity < 80)
  201. {
  202. step = CHARGE_STEP_FAST;
  203. ep->current_now = 2000000;
  204. }
  205. else if (ep->capacity < 95)
  206. {
  207. step = CHARGE_STEP_NORMAL;
  208. ep->current_now = 1000000;
  209. }
  210. else
  211. {
  212. step = CHARGE_STEP_TRICKLE;
  213. ep->current_now = 500000;
  214. }
  215. ep->capacity = rt_min_t(rt_uint32_t,
  216. ep->capacity + (step * elapsed_ms) / POLL_INTERVAL_MS, 100);
  217. ep->voltage = emu_battery_info.voltage_max_design_uv - (100 - ep->capacity) * 30;
  218. if (ep->capacity >= 100)
  219. {
  220. ep->status = RT_POWER_SUPPLY_STATUS_FULL;
  221. }
  222. else
  223. {
  224. ep->status = RT_POWER_SUPPLY_STATUS_CHARGING;
  225. }
  226. }
  227. else
  228. {
  229. int drain = (avg_load * DISCHARGE_BASE_RATE * elapsed_ms) / 1000;
  230. ep->capacity = rt_max_t(rt_uint32_t, ep->capacity - drain, 0);
  231. ep->current_now = -(500000 + (avg_load * 5000));
  232. ep->voltage = emu_battery_info.voltage_min_design_uv + ep->capacity * 1200;
  233. ep->status = (ep->capacity > 0) ?
  234. RT_POWER_SUPPLY_STATUS_DISCHARGING : RT_POWER_SUPPLY_STATUS_NOT_CHARGING;
  235. }
  236. ep->temp = 25000 + (ep->current_now / 1000) * TEMP_SENSITIVITY;
  237. ep->temp = rt_clamp((rt_uint32_t)ep->temp,
  238. (rt_uint32_t)emu_battery_info.temp_min, (rt_uint32_t)emu_battery_info.temp_max);
  239. rt_power_supply_changed(&ep->charger);
  240. rt_power_supply_changed(&ep->battery);
  241. }
  242. static int emu_power_init(void)
  243. {
  244. struct rt_cpu_usage_stats *stats = &rt_cpu_self()->cpu_stat;
  245. struct emu_power *ep = &_emu_power;
  246. rt_memset(ep, 0, sizeof(*ep));
  247. rt_dm_dev_set_name(&ep->parent, "emu-power");
  248. ep->battery.dev = &ep->parent,
  249. ep->battery.type = RT_POWER_SUPPLY_TYPE_BATTERY,
  250. ep->battery.properties_nr = RT_ARRAY_SIZE(emu_battery_properties),
  251. ep->battery.properties = emu_battery_properties,
  252. ep->battery.battery_info = &emu_battery_info,
  253. ep->battery.ops = &emu_battery_ops,
  254. ep->charger.dev = &ep->parent,
  255. ep->charger.type = RT_POWER_SUPPLY_TYPE_USB_SDP,
  256. ep->charger.properties_nr = RT_ARRAY_SIZE(emu_charger_properties),
  257. ep->charger.properties = emu_charger_properties,
  258. ep->charger.ops = &emu_charger_ops,
  259. ep->status = RT_POWER_SUPPLY_STATUS_DISCHARGING;
  260. ep->health = RT_POWER_SUPPLY_HEALTH_GOOD;
  261. ep->present = 1;
  262. ep->capacity = 100;
  263. ep->voltage = 3800000;
  264. ep->voltage_max = emu_battery_info.voltage_max_design_uv;
  265. ep->current_max = emu_battery_info.constant_charge_current_max_ua;
  266. ep->temp = 25000;
  267. ep->last_poll_tick = rt_tick_get();
  268. ep->last_idle = stats->idle;
  269. ep->last_total = stats->user + stats->system + stats->irq + stats->idle;
  270. rt_power_supply_register(&ep->battery);
  271. rt_power_supply_register(&ep->charger);
  272. rt_timer_init(&ep->poller, ep->parent.parent.name, &emu_power_poll, ep,
  273. rt_tick_from_millisecond(POLL_INTERVAL_MS), RT_TIMER_FLAG_PERIODIC);
  274. rt_timer_start(&ep->poller);
  275. return 0;
  276. }
  277. INIT_DEVICE_EXPORT(emu_power_init);
  278. static int emu_charger(int argc, char**argv)
  279. {
  280. rt_base_t level;
  281. struct emu_power *ep = &_emu_power;
  282. if (argc != 2)
  283. {
  284. goto _help;
  285. }
  286. level = rt_hw_interrupt_disable();
  287. if (!rt_strcmp(argv[1], "on"))
  288. {
  289. ep->status = RT_POWER_SUPPLY_STATUS_CHARGING;
  290. ep->ac_online = 1;
  291. ep->current_max = emu_battery_info.constant_charge_current_max_ua;
  292. }
  293. else if (!rt_strcmp(argv[1], "off"))
  294. {
  295. ep->status = RT_POWER_SUPPLY_STATUS_DISCHARGING;
  296. ep->ac_online = 0;
  297. ep->current_max = 0;
  298. }
  299. else
  300. {
  301. rt_hw_interrupt_enable(level);
  302. goto _help;
  303. }
  304. rt_hw_interrupt_enable(level);
  305. return 0;
  306. _help:
  307. rt_kprintf("Usage: %s [on|off]\n", __func__);
  308. return -1;
  309. }
  310. MSH_CMD_EXPORT(emu_charger, emu charger switch);