main.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. /*
  2. ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. #include "ch.h"
  14. #include "hal.h"
  15. #include "chprintf.h"
  16. #include "gfx.h"
  17. #include "buttons.h"
  18. #include "INA3221.h"
  19. #define INA_SCAN_PERIOS_US 62500
  20. #define INA_AVG_FACTOR 16
  21. #define CHRGR_PAUSE1_S 300
  22. #define CHRGR_PAUSE2_S 300
  23. #define INA_ALL_VALUSE EVENT_MASK(0)
  24. #define INA_BUS_VALUES EVENT_MASK(1)
  25. #define CHRGR_ST_CHANGE EVENT_MASK(2)
  26. #define TIME_CHANGE EVENT_MASK(3)
  27. /* Type definitions */
  28. typedef enum chrgr_state {
  29. Stop = 0,
  30. Charge1,
  31. Pause1,
  32. Decharge,
  33. Pause2,
  34. Charge2,
  35. Error
  36. } charger_state_t;
  37. typedef struct timer {
  38. uint8_t hh;
  39. uint8_t mm;
  40. uint8_t ss;
  41. } time_cnt_t;
  42. typedef struct accum_profile {
  43. char * Chemistry;
  44. char * VoltageNominal;
  45. uint32_t Capacity_mAh;
  46. uint32_t VoltageMax_mv;
  47. uint32_t VoltageDechMin_mv;
  48. uint32_t CurrentMax_mA;
  49. uint32_t CurrentChrgMin_mA;
  50. time_cnt_t TimeChargeMax;
  51. char * Description;
  52. } accum_profile_t;
  53. #define ACCUM_PRIFILE_NUM 4
  54. accum_profile_t Profile[ACCUM_PRIFILE_NUM] = {
  55. {"SLA", "12V", 12000, 15000, 10500, 3600, 240, {24,0,0}, "GP12120"},
  56. {"SLA", "6V", 1200, 7500, 5250, 360, 24, {24,0,0}, "GP6012"},
  57. {"LiIon", "1S1", 3200, 4200, 2500, 1625, 65, {4,0,0}, "NCR18650B"},
  58. {"LiIon", "1S2", 2600, 4200, 2750, 1300, 130, {3,0,0}, "ICR18650-26F"}
  59. };
  60. enum menu_items {
  61. MenuItem_1 = 0,
  62. MenuItem_2 = 1,
  63. MenuItem_3 = 2,
  64. MenuItem_4 = 3,
  65. MenuItems = 4
  66. };
  67. typedef struct {
  68. color_t activeFG;
  69. color_t activeBG;
  70. color_t inactiveFG;
  71. color_t inactiveBG;
  72. } menu_colors_t;
  73. // types and description of accums - SLA, LiIon
  74. // variants and description of voltaged accroding to cemistry type - 6/12V, 1-4S
  75. // and db of min and max voltages for each
  76. // some set of amperage or capacitance - range of current for charge/decharge stop condition
  77. typedef struct chrgr_channel {
  78. int16_t shunt; /**< resistance in milliOhms. */
  79. int16_t max_current; /**< max channel current in milliAmpers. */
  80. } charger_channel_t;
  81. /*
  82. * I2C1 config.
  83. */
  84. static const I2CConfig i2cfg1 = {
  85. OPMODE_I2C,
  86. 400000,
  87. FAST_DUTY_CYCLE_2,
  88. };
  89. /*
  90. *
  91. */
  92. static void gpt_cb(GPTDriver *gptp);
  93. static GPTConfig gpt_cfg = {
  94. 1000000, /* Timer clock 1 MHz. */
  95. gpt_cb, /* Timer callback. */
  96. 0, 0
  97. };
  98. /* Private constants */
  99. static const charger_channel_t charger_Channels[INA3221_CH_NUM] = {
  100. {20, 8190}, {50, 3276}, {10, 16380}
  101. };
  102. /* Privae functions */
  103. static void prepare_Screen(void);
  104. static void ina_Process(void);
  105. static void mode_vt_cb(virtual_timer_t *vtp, void *st);
  106. static void btn1_handler(const button_state_t);
  107. static void btn2_handler(const button_state_t);
  108. static void show_MenuItem(const int item);
  109. /* Private variables */
  110. static binary_semaphore_t ina_bsem, charger_bsem;
  111. static virtual_timer_t mode_vt;
  112. static event_source_t ina_all_event, ina_bus_event;
  113. static event_source_t chrgr_st_event, time_event;
  114. static charger_state_t charger_State;
  115. static ina3221_ch_t charger_Channel;
  116. static int charger_Profile = 0;
  117. static uint32_t Current;
  118. static uint32_t Voltage;
  119. static uint32_t Power, Capacity_I=0, Capacity_P=0;
  120. static uint32_t dech_Capacity_I, dech_Capacity_P, ch_Capacity_I, ch_Capacity_P;
  121. static int INA_Present = 0;
  122. static BaseSequentialStream * chp = (BaseSequentialStream*) &SD1;
  123. GHandle GW1; // The handle for our console
  124. ina3221_t ina3221;
  125. static gFont font1;
  126. static gFont font2;
  127. static time_cnt_t Timer = {0}, dechTimer, chTimer;
  128. static btn_hndlr bha[Button_Num] = {btn1_handler, btn2_handler};
  129. static int menu_Active = MenuItem_1;
  130. static menu_colors_t menu_Colors = {Navy, Olive, Yellow, Navy};
  131. /*
  132. * INA process thread.
  133. */
  134. static THD_WORKING_AREA(waInaThread, 256);
  135. static THD_FUNCTION(InaThread, arg) {
  136. (void)arg;
  137. while (true) {
  138. chBSemWait(&ina_bsem);
  139. ina_Process();
  140. }
  141. }
  142. /*
  143. * Charger process thread. Once per second.
  144. */
  145. static THD_WORKING_AREA(waChrgThread, 256);
  146. static THD_FUNCTION(ChrgThread, arg) {
  147. (void)arg;
  148. while (true) {
  149. chBSemWait(&charger_bsem);
  150. if (charger_State != Stop) {
  151. if ((charger_State == Pause1) || (charger_State == Pause2)) {
  152. /* Pause countdown */
  153. if (Timer.ss > 0) {
  154. Timer.ss --;
  155. } else {
  156. if (Timer.mm > 0) {
  157. Timer.mm --;
  158. } else {
  159. if (Timer.hh > 0){
  160. Timer.hh --;
  161. } else {
  162. break;
  163. }
  164. Timer.mm = 59;
  165. }
  166. Timer.ss = 59;
  167. }
  168. } else {
  169. /* Charge/Decharge counter */
  170. if (Timer.ss < 59) {
  171. Timer.ss ++;
  172. } else {
  173. Timer.ss = 0;
  174. if (Timer.mm < 59) {
  175. Timer.mm ++;
  176. } else {
  177. Timer.mm = 0;
  178. Timer.hh ++;
  179. }
  180. }
  181. }
  182. /* Show new timer value */
  183. chEvtBroadcast(&time_event);
  184. }
  185. }
  186. }
  187. /*
  188. * Green LED blinker thread, times are in milliseconds.
  189. */
  190. static THD_WORKING_AREA(waThread1, 128);
  191. static THD_FUNCTION(Thread1, arg) {
  192. (void)arg;
  193. chRegSetThreadName("blinker");
  194. while (true) {
  195. switch (charger_State) {
  196. case Stop:
  197. palClearLine(LINE_LED1);
  198. chThdSleepMilliseconds(500);
  199. palSetLine(LINE_LED1);
  200. chThdSleepMilliseconds(500);
  201. break;
  202. case Error:
  203. palClearLine(LINE_LED1);
  204. chThdSleepMilliseconds(200);
  205. palSetLine(LINE_LED1);
  206. chThdSleepMilliseconds(200);
  207. break;
  208. case Decharge:
  209. palClearLine(LINE_LED1);
  210. chThdSleepMilliseconds(900);
  211. palSetLine(LINE_LED1);
  212. chThdSleepMilliseconds(100);
  213. break;
  214. default:
  215. palClearLine(LINE_LED1);
  216. chThdSleepMilliseconds(100);
  217. palSetLine(LINE_LED1);
  218. chThdSleepMilliseconds(900);
  219. break;
  220. }
  221. }
  222. }
  223. /*
  224. * Application entry point.
  225. */
  226. int main(void) {
  227. /*
  228. * System initializations.
  229. * - HAL initialization, this also initializes the configured device drivers
  230. * and performs the board-specific initializations.
  231. * - Kernel initialization, the main() function becomes a thread and the
  232. * RTOS is active.
  233. */
  234. halInit();
  235. chSysInit();
  236. /*
  237. * This initialization requires the OS already active because it uses delay
  238. * APIs inside.
  239. *
  240. * Initialize uGFX and the underlying system
  241. */
  242. gfxInit();
  243. prepare_Screen();
  244. /*
  245. * Activates the I2C driver 1.
  246. */
  247. i2cStart(&I2CD1, &i2cfg1);
  248. /*
  249. * Activates the serial driver 1 using the driver default configuration.
  250. */
  251. sdStart(&SD1, NULL);
  252. chprintf(chp, "\033[2J\033[1;1H"); // clear screen
  253. chprintf(chp, "Started\r\n");
  254. /*
  255. * Starting the button handle thread.
  256. */
  257. buttons_Init(bha);
  258. /*
  259. * Initializing semaphores in the "taken" state.
  260. */
  261. chBSemObjectInit(&ina_bsem, true);
  262. chBSemObjectInit(&charger_bsem, true);
  263. /*
  264. * Creates the blinker thread.
  265. */
  266. chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO-1, Thread1, NULL);
  267. /*
  268. * Creates the charger thread.
  269. */
  270. chThdCreateStatic(waChrgThread, sizeof(waChrgThread), NORMALPRIO, ChrgThread, NULL);
  271. /*
  272. * Creates the INA thread.
  273. */
  274. chThdCreateStatic(waInaThread, sizeof(waInaThread), NORMALPRIO, InaThread, NULL);
  275. //chVTSetContinuous(&ina_vt, TIME_MS2I(INA_SCAN_PERIOS_MS), ina_Process, NULL);
  276. gwinPrintf(GW1, "Try to init INA3221...\n");
  277. ina3221.i2c_addr = INA3221_ADDR40_GND;
  278. ina3221.i2cd = &I2CD1;
  279. ina3221.shuntRes[INA3221_CH1] = charger_Channels[INA3221_CH1].shunt;
  280. ina3221.shuntRes[INA3221_CH2] = charger_Channels[INA3221_CH2].shunt;
  281. ina3221.shuntRes[INA3221_CH3] = charger_Channels[INA3221_CH3].shunt;
  282. if (INA3221_getManufID(&ina3221) == 0x5449) {
  283. if (INA3221_getDieID(&ina3221) == 0x3220) {
  284. INA_Present = 1;
  285. gwinPrintf(GW1, "INA3221 present!\n");
  286. INA3221_reset(&ina3221);
  287. INA3221_setBusConversionTime(&ina3221, INA3221_REG_CONF_CT_8244US);
  288. }
  289. } else {
  290. gwinPrintf(GW1, "Sensor INA3221 not give proper IDs.\n", Voltage);
  291. }
  292. /* Starting GPT for ina scan and seconds counter */
  293. gptStart(&GPTD7, &gpt_cfg);
  294. gptStartContinuous(&GPTD7, INA_SCAN_PERIOS_US);
  295. /* Init charger module */
  296. charger_State = Stop;
  297. charger_Channel = INA3221_CH1;
  298. event_listener_t el0, el1, el2, el3;
  299. /* Events initialization and registration.*/
  300. chEvtObjectInit(&ina_all_event);
  301. chEvtObjectInit(&ina_bus_event);
  302. chEvtObjectInit(&chrgr_st_event);
  303. chEvtObjectInit(&time_event);
  304. chEvtRegister(&ina_all_event, &el0, 0);
  305. chEvtRegister(&ina_bus_event, &el1, 1);
  306. chEvtRegister(&chrgr_st_event, &el2, 2);
  307. chEvtRegister(&time_event, &el3, 3);
  308. eventmask_t events;
  309. charger_state_t oldState = Stop;
  310. char buf[16];
  311. uint32_t tmp0, tmp1, tmp2;
  312. /*
  313. * Normal main() thread activity, in this demo it does nothing except
  314. * sleeping in a loop and check the button state.
  315. */
  316. while (true) {
  317. //chThdSleepMilliseconds(500);
  318. events = chEvtWaitAny(ALL_EVENTS);
  319. if (events & INA_ALL_VALUSE) {
  320. tmp1 = Voltage / 1000;
  321. tmp2 = Voltage % 1000;
  322. chsnprintf(buf, 11, "U:%2d.%03uV", tmp1, tmp2);
  323. gdispFillStringBox(1, 123, 158, 29, buf, font2, Red, Gray, gJustifyLeft);
  324. tmp1 = Current / 1000;
  325. tmp2 = Current % 1000;
  326. chsnprintf(buf, 11, "I:%2d.%03uA", tmp1, tmp2);
  327. gdispFillStringBox(1, 153, 158, 29, buf, font2, Red, Gray, gJustifyLeft);
  328. tmp1 = Power / 1000;
  329. tmp2 = Power % 1000;
  330. chsnprintf(buf, 11, "P:%2d.%03uW", tmp1, tmp2);
  331. gdispFillStringBox(1, 183, 158, 29, buf, font2, Red, Gray, gJustifyLeft);
  332. tmp0 = (Capacity_I + 1800) / 3600;
  333. tmp1 = tmp0 / 1000;
  334. tmp2 = tmp0 % 1000;
  335. chsnprintf(buf, 13, "CI:%2d.%03uAh", tmp1, tmp2);
  336. gdispFillStringBox(160, 153, 158, 29, buf, font2, Red, Gray, gJustifyLeft);
  337. tmp0 = (Capacity_P + 1800) / 3600;
  338. tmp1 = tmp0 / 1000;
  339. tmp2 = tmp0 % 1000;
  340. chsnprintf(buf, 13, "CP:%2d.%03uWh", tmp1, tmp2);
  341. gdispFillStringBox(160, 183, 158, 29, buf, font2, Red, Gray, gJustifyLeft);
  342. /* Check charge/decharge conditions */
  343. switch (charger_State) {
  344. case Charge1:
  345. case Charge2:
  346. if (Profile[charger_Profile].VoltageMax_mv < Voltage) {
  347. gwinPrintf(GW1, "Overvoltage detected!\n");
  348. charger_State = Error;
  349. events |= CHRGR_ST_CHANGE;
  350. }
  351. if (Profile[charger_Profile].CurrentMax_mA < Current) {
  352. gwinPrintf(GW1, "Overcurrent detected!\n");
  353. charger_State = Error;
  354. events |= CHRGR_ST_CHANGE;
  355. }
  356. if (Profile[charger_Profile].CurrentChrgMin_mA > Current) {
  357. gwinPrintf(GW1, "Charged Successful.\n");
  358. if (charger_State == Charge1) {
  359. charger_State = Pause1;
  360. } else {
  361. charger_State = Stop;
  362. }
  363. events |= CHRGR_ST_CHANGE;
  364. }
  365. break;
  366. case Decharge:
  367. if (Profile[charger_Profile].VoltageDechMin_mv > Voltage) {
  368. gwinPrintf(GW1, "Decharge finished.\n");
  369. charger_State = Pause2;
  370. events |= CHRGR_ST_CHANGE;
  371. }
  372. break;
  373. default:
  374. break;
  375. }
  376. }
  377. if (events & INA_BUS_VALUES) {
  378. tmp1 = Voltage / 1000;
  379. tmp2 = Voltage % 1000;
  380. chsnprintf(buf, 11, "U:%2d.%03uV", tmp1, tmp2);
  381. gdispFillStringBox(1, 123, 158, 29, buf, font2, Red, Gray, gJustifyLeft);
  382. /* here should control processes time ? */
  383. }
  384. if (events & CHRGR_ST_CHANGE) {
  385. switch (charger_State) {
  386. case Charge1:
  387. if (Profile[charger_Profile].VoltageDechMin_mv > Voltage) {
  388. gwinPrintf(GW1, "No Battery detected / Low voltage...\n");
  389. charger_State = Stop;
  390. gdispFillStringBox(1, 213, 318, 29, "Stop", font2, Red, Silver, gJustifyCenter);
  391. break;
  392. }
  393. oldState = Charge1;
  394. Capacity_I = 0;
  395. Capacity_P = 0;
  396. Timer.hh = 0;
  397. Timer.mm = 0;
  398. Timer.ss = 0;
  399. gwinPrintf(GW1, "Begin process: Charge_1\n");
  400. palClearLine(LINE_RELAY1); // power relay on
  401. gdispFillStringBox(1, 213, 318, 29, "Charge 1", font2, Red, Silver, gJustifyCenter);
  402. break;
  403. case Pause1:
  404. oldState = Pause1;
  405. palSetLine(LINE_RELAY1); // power relay off
  406. tmp0 = CHRGR_PAUSE1_S / 60;
  407. Timer.ss = CHRGR_PAUSE1_S % 60;
  408. Timer.hh = tmp0 / 60;
  409. Timer.mm = tmp0 % 60;
  410. tmp0 = (Capacity_I + 1800) / 3600;
  411. tmp1 = tmp0 / 1000;
  412. tmp2 = (tmp0 % 1000) / 10;
  413. gwinPrintf(GW1, "CI/CP: %2u.%02u", tmp1, tmp2);
  414. tmp0 = (Capacity_P + 1800) / 3600;
  415. tmp1 = tmp0 / 1000;
  416. tmp2 = (tmp0 % 1000) / 10;
  417. gwinPrintf(GW1, "/%2u.%02u\n", tmp1, tmp2);
  418. gwinPrintf(GW1, "Pause after Charge_1\n");
  419. gdispFillStringBox(1, 213, 318, 29, "Pause 1", font2, Red, Silver, gJustifyCenter);
  420. chVTSet(&mode_vt, TIME_S2I(CHRGR_PAUSE1_S), mode_vt_cb, (void *)Decharge);
  421. break;
  422. case Decharge:
  423. oldState = Decharge;
  424. Capacity_I = 0;
  425. Capacity_P = 0;
  426. Timer.hh = 0;
  427. Timer.mm = 0;
  428. Timer.ss = 0;
  429. gwinPrintf(GW1, "Begin process: Decharge\n");
  430. palClearLine(LINE_RELAY2); // load relay on
  431. gdispFillStringBox(1, 213, 318, 29, "Decharge", font2, Red, Silver, gJustifyCenter);
  432. break;
  433. case Pause2:
  434. oldState = Pause2;
  435. palSetLine(LINE_RELAY2); // load relay off
  436. dech_Capacity_I = Capacity_I;
  437. dech_Capacity_P = Capacity_P;
  438. dechTimer = Timer;
  439. tmp0 = CHRGR_PAUSE2_S / 60;
  440. Timer.ss = CHRGR_PAUSE2_S % 60;
  441. Timer.hh = tmp0 / 60;
  442. Timer.mm = tmp0 % 60;
  443. tmp0 = (Capacity_I + 1800) / 3600;
  444. tmp1 = tmp0 / 1000;
  445. tmp2 = (tmp0 % 1000) / 10;
  446. gwinPrintf(GW1, "CI/CP: %2u.%02u", tmp1, tmp2);
  447. tmp0 = (Capacity_P + 1800) / 3600;
  448. tmp1 = tmp0 / 1000;
  449. tmp2 = (tmp0 % 1000) / 10;
  450. gwinPrintf(GW1, "/%2u.%02u\n", tmp1, tmp2);
  451. gwinPrintf(GW1, "Pause after Decharge\n");
  452. gdispFillStringBox(1, 213, 318, 29, "Pause 2", font2, Red, Silver, gJustifyCenter);
  453. chVTSet(&mode_vt, TIME_S2I(CHRGR_PAUSE2_S), mode_vt_cb, (void *)Charge2);
  454. break;
  455. case Charge2:
  456. Capacity_I = 0;
  457. Capacity_P = 0;
  458. Timer.hh = 0;
  459. Timer.mm = 0;
  460. Timer.ss = 0;
  461. oldState = Charge2;
  462. gwinPrintf(GW1, "Begin process: Charge_2\n");
  463. palClearLine(LINE_RELAY1); // power relay on
  464. gdispFillStringBox(1, 213, 318, 29, "Charge 2", font2, Red, Silver, gJustifyCenter);
  465. break;
  466. case Error:
  467. oldState = Error;
  468. // power and load relay off
  469. palSetLine(LINE_RELAY1);
  470. palSetLine(LINE_RELAY2);
  471. gdispFillStringBox(1, 213, 318, 29, "Error", font2, Silver, Red, gJustifyCenter);
  472. break;
  473. default:
  474. if (oldState == Charge2) {
  475. ch_Capacity_I = Capacity_I;
  476. ch_Capacity_P = Capacity_P;
  477. chTimer = Timer;
  478. Timer.hh = 0;
  479. Timer.mm = 0;
  480. Timer.ss = 0;
  481. tmp0 = (Capacity_I + 1800) / 3600;
  482. tmp1 = tmp0 / 1000;
  483. tmp2 = (tmp0 % 1000) / 10;
  484. gwinPrintf(GW1, "CI/CP: %2u.%02u", tmp1, tmp2);
  485. tmp0 = (Capacity_P + 1800) / 3600;
  486. tmp1 = tmp0 / 1000;
  487. tmp2 = (tmp0 % 1000) / 10;
  488. gwinPrintf(GW1, "/%2u.%02u\n", tmp1, tmp2);
  489. }
  490. oldState = Stop;
  491. // power and load relay off
  492. palSetLine(LINE_RELAY1);
  493. palSetLine(LINE_RELAY2);
  494. gdispFillStringBox(1, 213, 318, 29, "Stop", font2, Red, Silver, gJustifyCenter);
  495. break;
  496. }
  497. }
  498. if (events & TIME_CHANGE) {
  499. chsnprintf(buf, 12, "T:%02u:%02u:%02u", Timer.hh, Timer.mm, Timer.ss);
  500. gdispFillStringBox(160, 123, 158, 29, buf, font2, Red, Gray, gJustifyLeft);
  501. }
  502. }
  503. }
  504. /**
  505. * @brief Prepare screen for inforation presentation.
  506. */
  507. static void prepare_Screen(void) {
  508. /* Set some fonts */
  509. font1 = gdispOpenFont("DejaVu Sans Book 12");
  510. font2 = gdispOpenFont("DejaVu Sans Book 24");
  511. gwinSetDefaultFont(font1);
  512. /* draw screen frame */
  513. const coord_t width = 320;
  514. const coord_t height = 240;
  515. gdispDrawBox(0, 0, width-1, height-1, Green); // all screen
  516. gdispDrawLine(0, 122, width-1, 122, Green); // h line
  517. gdispDrawLine(79, 0, 79, 122, Green); // v line, text | console
  518. gdispFillArea(1, 1, 78, 121, Blue); // text area
  519. /* print text to top-left area */
  520. show_MenuItem(MenuItem_1);
  521. show_MenuItem(MenuItem_2);
  522. show_MenuItem(MenuItem_3);
  523. show_MenuItem(MenuItem_4);
  524. /* clear bootom area */
  525. gdispFillStringBox(1, 123, 318, 115, " ", font2, Red, Gray, gJustifyLeft);
  526. gdispFillStringBox(1, 213, 318, 29, "Stop", font2, Red, Silver, gJustifyCenter);
  527. /* create the console window */
  528. {
  529. GWindowInit wi;
  530. gwinClearInit(&wi);
  531. wi.show = gTrue;
  532. wi.x = 82; wi.y = 2; wi.width = 239; wi.height = 120;
  533. GW1 = gwinConsoleCreate(0, &wi);
  534. }
  535. /* Set the fore- and background colors for each console */
  536. gwinSetColor(GW1, GFX_WHITE);
  537. gwinSetBgColor(GW1, GFX_BLACK);
  538. /* clear all console windows - to set background */
  539. gwinClear(GW1);
  540. /* Output some data on the console */
  541. gwinPrintf(GW1, "\033bConsole\033B started sucessful.\n");
  542. }
  543. /**
  544. * @brief Select next menu item
  545. */
  546. static void btn1_handler(button_state_t state) {
  547. if (state == BTN_st_Pressed) {
  548. switch (menu_Active) {
  549. case MenuItem_1:
  550. menu_Active = MenuItem_2;
  551. show_MenuItem(MenuItem_1);
  552. show_MenuItem(MenuItem_2);
  553. break;
  554. case MenuItem_2:
  555. menu_Active = MenuItem_3;
  556. show_MenuItem(MenuItem_2);
  557. show_MenuItem(MenuItem_3);
  558. break;
  559. case MenuItem_3:
  560. menu_Active = MenuItem_4;
  561. show_MenuItem(MenuItem_3);
  562. show_MenuItem(MenuItem_4);
  563. break;
  564. case MenuItem_4:
  565. menu_Active = MenuItem_1;
  566. show_MenuItem(MenuItem_4);
  567. show_MenuItem(MenuItem_1);
  568. break;
  569. default:
  570. menu_Active = MenuItem_1;
  571. break;
  572. }
  573. }
  574. }
  575. /**
  576. * @brief Process selected menu item
  577. */
  578. static void btn2_handler(button_state_t state) {
  579. static int s3 = 0; // menu_item3 state
  580. if (state == BTN_st_Pressed) {
  581. switch (menu_Active) {
  582. case MenuItem_1:
  583. if (charger_State == Stop) {
  584. switch (charger_Channel) {
  585. case INA3221_CH1:
  586. charger_Channel = INA3221_CH2;
  587. break;
  588. case INA3221_CH2:
  589. charger_Channel = INA3221_CH3;
  590. break;
  591. case INA3221_CH3:
  592. charger_Channel = INA3221_CH1;
  593. break;
  594. default:
  595. charger_Channel = INA3221_CH1;
  596. break;
  597. }
  598. gwinPrintf(GW1, "Channel #%u selected.\n", (int)charger_Channel + 1);
  599. show_MenuItem(MenuItem_1);
  600. }
  601. break;
  602. case MenuItem_2:
  603. charger_Profile ++;
  604. if (charger_Profile >= ACCUM_PRIFILE_NUM) {
  605. charger_Profile = 0;
  606. }
  607. gwinPrintf(GW1, "Select profile for %s\n", Profile[charger_Profile].Description);
  608. show_MenuItem(MenuItem_2);
  609. break;
  610. case MenuItem_3:
  611. if (s3 == 0) {
  612. s3 ++;
  613. gwinPrintf(GW1, "Show Charge results\n");
  614. Capacity_I = ch_Capacity_I;
  615. Capacity_P = ch_Capacity_P;
  616. Timer = chTimer;
  617. } else {
  618. s3 = 0;
  619. gwinPrintf(GW1, "Show Decharge results\n");
  620. Capacity_I = dech_Capacity_I;
  621. Capacity_P = dech_Capacity_P;
  622. Timer = dechTimer;
  623. }
  624. chEvtBroadcast(&ina_all_event);
  625. chEvtBroadcast(&time_event);
  626. break;
  627. case MenuItem_4:
  628. switch(charger_State) {
  629. case Stop:
  630. charger_State = Charge1;
  631. show_MenuItem(MenuItem_4);
  632. break;
  633. default:
  634. charger_State = Stop;
  635. Current = 0;
  636. Voltage = 0;
  637. Power = 0;
  638. show_MenuItem(MenuItem_4);
  639. break;
  640. }
  641. chEvtBroadcast(&chrgr_st_event);
  642. break;
  643. default:
  644. break;
  645. } /* switch menu_Active */
  646. } /* BTN pressed */
  647. } /* end of btn2_handler() */
  648. /**
  649. * @brief Virtual timer callback. Change charger state.
  650. */
  651. static void mode_vt_cb(virtual_timer_t *vtp, void * st) {
  652. (void)vtp;
  653. charger_State = (charger_state_t)st; // new charger state
  654. chSysLockFromISR();
  655. chEvtBroadcastI(&chrgr_st_event);
  656. chSysUnlockFromISR();
  657. }
  658. /**
  659. * @brief Show/update menu items.
  660. */
  661. static void show_MenuItem(const int item) {
  662. char buf1[16] = {0};
  663. char buf2[16] = {0};
  664. int tmp;
  665. coord_t y1=0, y2=0;
  666. static const coord_t x=1, cx=78, cy=15;
  667. static const justify_t j1 = gJustifyLeft;
  668. justify_t j2 = gJustifyLeft;
  669. color_t fgc, bgc;
  670. if (item == menu_Active) {
  671. fgc = menu_Colors.activeFG;
  672. bgc = menu_Colors.activeBG;
  673. } else {
  674. fgc = menu_Colors.inactiveFG;
  675. bgc = menu_Colors.inactiveBG;
  676. }
  677. switch (item) {
  678. case MenuItem_1:
  679. tmp = (int)charger_Channel + 1;
  680. chsnprintf(buf1, 10, "[1] ch.%u", tmp);
  681. chsnprintf(buf2, 12, "%2uA", charger_Channels[charger_Channel].max_current/1000);
  682. y1 = 1;
  683. y2 = 16;
  684. j2 = gJustifyCenter;
  685. break;
  686. case MenuItem_2:
  687. chsnprintf(buf1, 10, "[2] %s", Profile[charger_Profile].Chemistry);
  688. tmp = Profile[charger_Profile].Capacity_mAh / 1000;
  689. chsnprintf(buf2, 12, "%s/%uAh", Profile[charger_Profile].VoltageNominal, tmp);
  690. y1 = 31;
  691. y2 = 46;
  692. j2 = gJustifyCenter;
  693. break;
  694. case MenuItem_3:
  695. chsnprintf(buf1, 12, "[3] show ch");
  696. chsnprintf(buf2, 12, "dech results");
  697. y1 = 61;
  698. y2 = 76;
  699. break;
  700. case MenuItem_4:
  701. if (charger_State == Stop) {
  702. chsnprintf(buf1, 12, "[4] Start");
  703. } else {
  704. chsnprintf(buf1, 12, "[4] Stop");
  705. }
  706. //chsnprintf(buf2, 12, " ");
  707. y1 = 91;
  708. y2 = 106;
  709. break;
  710. default:
  711. break;
  712. }
  713. gdispFillStringBox(x, y1, cx, cy, buf1, font1, fgc, bgc, j1);
  714. // gdispFillStringBox(x, y2, cx, cy, buf2, font1, fgc, bgc, j2);
  715. gdispFillStringBox(x, y2, cx, cy, buf2, font1, menu_Colors.inactiveFG, menu_Colors.inactiveBG, j2);
  716. }
  717. /**
  718. * @brief Read Voltage & Current, update screen info.
  719. */
  720. static void ina_Process(void) {
  721. int32_t current;
  722. uint32_t volt;
  723. static int idx = 0;
  724. static uint32_t sumCurrent=0, sumVoltage=0;
  725. if (INA_Present != 0) {
  726. current = INA3221_getCurrent(&ina3221, charger_Channel);
  727. volt = INA3221_getVoltage(&ina3221, charger_Channel);
  728. if (current < 0) {
  729. current = 1 - current;
  730. }
  731. sumCurrent += current;
  732. sumVoltage += volt;
  733. idx ++;
  734. if (idx >= INA_AVG_FACTOR) {
  735. idx = 0;
  736. /* Count, summarize & show values */
  737. if (charger_State != Stop) {
  738. Current = (sumCurrent + (INA_AVG_FACTOR/2)) / INA_AVG_FACTOR;
  739. Voltage = (sumVoltage + (INA_AVG_FACTOR/2)) / INA_AVG_FACTOR;
  740. sumCurrent = 0;
  741. sumVoltage = 0;
  742. Power = ((Voltage * Current) + 500) / 1000;
  743. Capacity_I += Current;
  744. Capacity_P += Power;
  745. chEvtBroadcast(&ina_all_event);
  746. } else {
  747. /* Show bus voltage in STOP mode */
  748. Voltage = (sumVoltage + (INA_AVG_FACTOR/2)) / INA_AVG_FACTOR;
  749. sumCurrent=0;
  750. sumVoltage=0;
  751. chEvtBroadcast(&ina_bus_event);
  752. }
  753. }
  754. }
  755. }
  756. /**
  757. * @brief GPT callback. One 16 pulse per second.
  758. */
  759. static void gpt_cb(GPTDriver *gptp) {
  760. (void)gptp;
  761. static int cnt = 0;
  762. chSysLockFromISR();
  763. chBSemSignalI(&ina_bsem);
  764. chSysUnlockFromISR();
  765. cnt ++;
  766. if (cnt >= INA_AVG_FACTOR) {
  767. /* One second */
  768. cnt = 0;
  769. chSysLockFromISR();
  770. chBSemSignalI(&charger_bsem);
  771. chSysUnlockFromISR();
  772. }
  773. }