battery.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #include "battery.h"
  2. #include "ti/driverlib/dl_i2c.h"
  3. #include "ti_msp_dl_config.h"
  4. #include "src/peripherals/dac/dac.h"
  5. #include "src/peripherals/adc/adc.h"
  6. #include "src/interfaces/i2c_controller.h"
  7. // we need the itnerface for the ADC_TARGET_BASE_ADDRESS constant
  8. // refactor this somewhen to a general constants file
  9. #include "src/peripherals/adc/adc_interface.h"
  10. #include "src/config.h"
  11. BatterySlot battery_slots[NUM_SLOTS];
  12. static void set_dac(uint8_t slot, uint16_t value) {
  13. battery_slots[slot].dac_value = value;
  14. DAC_SingleWrite(slot, value);
  15. }
  16. static void set_pwm(uint8_t slot, uint16_t value) {
  17. battery_slots[slot].pwm_value = value;
  18. DL_TimerG_setCaptureCompareValue(battery_slots[0].timer, value, DL_TIMER_CC_1_INDEX);
  19. }
  20. static void batteryslots_disable(uint8_t slot) {
  21. if (battery_slots[slot].dac_value != 0) {
  22. set_dac(slot, 0);
  23. }
  24. if (battery_slots[slot].pwm_value != 0) {
  25. set_pwm(slot, 0);
  26. }
  27. }
  28. /*Initialize battery array and default parameters*/
  29. static void batteryslots_init() {
  30. // initialize data structures
  31. battery_slots[0].timer = PWM_0_INST;
  32. #if NUM_SLOTS == 4
  33. battery_slots[1].timer = PWM_1_INST;
  34. battery_slots[2].timer = PWM_2_INST;
  35. battery_slots[3].timer = PWM_3_INST;
  36. #endif
  37. for(uint8_t i=0; i< NUM_SLOTS; i++){
  38. battery_slots[i].measurement.state = SLOT_STATE_OK;
  39. // convinience trick:
  40. // with that we can set *battery_slots[i].state = SLOT_STATE_* or SLOT_ERR_*
  41. // like e.g. *battery_slots[i].state = SLOT_ERR_OVERTEMPERATURE
  42. battery_slots[i].state = &battery_slots[i].measurement.state;
  43. battery_slots[i].measurement.voltage = 0;
  44. battery_slots[i].measurement.current = 0;
  45. battery_slots[i].measurement.temperature = 0;
  46. battery_slots[i].set_current = 0;
  47. set_pwm(i, 0);
  48. }
  49. if (!i2c_discover(DAC_TARGET_ADDRESS)) {
  50. // there is only 1 DAC for all 4 slots
  51. for(uint8_t i=0; i< NUM_SLOTS; i++) {
  52. *battery_slots[i].state = SLOT_ERR_NO_DAC;
  53. }
  54. // Error state - no I2C on bus - we cannot continue.
  55. return;
  56. }
  57. for(uint8_t i=0; i< NUM_SLOTS; i++){
  58. set_dac(i, 0);
  59. if (!i2c_discover(ADC_TARGET_BASE_ADDRESS+i)) {
  60. // this automatically translates to the other addresses
  61. *battery_slots[i].state = SLOT_ERR_NO_ADC1+i;
  62. }
  63. }
  64. }
  65. static void batteryslots_read_state(uint8_t slot) {
  66. /*
  67. * Strategy:
  68. * 1. updateADCReading calls the ADC function that does the control loop for getting the values
  69. * 2. the updateADCReading also calls internally the HAL to send the i2c commands,
  70. * construct the configuration byte and calculate the values (voltage, current)
  71. * 3. the adc updates the battery slot value directly
  72. */
  73. // step 1: read channel 0 (voltage reading of the cell)
  74. uint16_t bare_voltage = read_adc_channel(slot, 0);
  75. battery_slots[slot].measurement.voltage = (100.0/56+1)*bare_voltage; // We have that voltage divider
  76. // DAC branch: we can calculate the current based on the shunt
  77. if (battery_slots[slot].set_current >= 0) {
  78. // read channel 1 (current reading on charge)
  79. bare_voltage = read_adc_channel(slot, 1);
  80. battery_slots[slot].measurement.current = bare_voltage*10/1000; // current comes in microvolts
  81. #ifdef DEBUG_TRACE_CTRL
  82. printf("Slot %d voltage: %d mV and %d mA (dac shunt)\n", slot, battery_slots[slot].measurement.voltage, battery_slots[slot].measurement.current);
  83. #endif
  84. } else {
  85. // we are in PWM mode, the shunt is on the high side
  86. // read channel 2 (voltage reading on 5V side)
  87. int16_t shunt_current = read_adc_channel(slot, 2)*10;
  88. // read channel 3 (current reading after step conversion, 5V side)
  89. int16_t hi_voltage = read_adc_channel(slot, 3)*100/56;
  90. // calculate the result
  91. battery_slots[slot].measurement.current = -1*shunt_current*hi_voltage/battery_slots[slot].measurement.voltage;
  92. #ifdef DEBUG_TRACE_CTRL
  93. printf("Slot %d voltage: %d mV and %d mA (pwm shunt) (hi side voltage: %d mV, hi side current: %d mA)\n", slot, battery_slots[slot].measurement.voltage, battery_slots[slot].measurement.current, hi_voltage, shunt_current);
  94. #endif
  95. }
  96. }
  97. static void batteryslots_adjust_current(uint8_t slot) {
  98. #ifdef DEBUG_TRACE_CTRL
  99. printf("Ctrl: Checking to adjust battery current. Set current: %d mA, measured current: %d mA\n",
  100. battery_slots[slot].set_current,
  101. battery_slots[slot].measurement.current
  102. );
  103. #endif
  104. if (battery_slots[slot].set_current > 0) {
  105. // positive current -> charge (with DAC)
  106. if (battery_slots[slot].pwm_value != 0) {
  107. // seems like we switched from a charging before
  108. // -> disable DAC before getting active
  109. #ifdef DEBUG_CTRL
  110. printf("Detected active pwm. Value: %d. Disabling.", battery_slots[slot].pwm_value);
  111. #endif
  112. set_pwm(slot, 0);
  113. }
  114. if (battery_slots[slot].set_current + BATTERY_CURRENT_THRESHOLD < battery_slots[slot].measurement.current) {
  115. // we are outside of the tolerance band
  116. // exceeded to the upper limit
  117. // -> update dac value, decrease the voltage
  118. if (battery_slots[slot].dac_value-1 >= 0) {
  119. #ifdef DEBUG_TRACE_CTRL
  120. printf("Ctrl: Decreasing DAC to %d\n", battery_slots[slot].dac_value-1);
  121. #endif
  122. set_dac(slot, --battery_slots[slot].dac_value);
  123. }
  124. else {
  125. // we want to give more current, but we can't ?!
  126. #ifdef DEBUG_CTRL
  127. printf("Ctrl: Unable to decrease DAC to %d\n", battery_slots[slot].dac_value-1);
  128. #endif
  129. *battery_slots[slot].state = SLOT_WARN_LOWER_DAC_NOT_POSSIBLE;
  130. }
  131. } else if (battery_slots[slot].set_current - BATTERY_CURRENT_THRESHOLD > battery_slots[slot].measurement.current) {
  132. // we are outside of the tolerance band
  133. // exceeded to the upplowerer limit
  134. // -> update dac value, increase the voltage
  135. if (battery_slots[slot].dac_value+1 <= MAX_DAC_VALUE) {
  136. #ifdef DEBUG_TRACE_CTRL
  137. printf("Ctrl: Increasing DAC to %d\n", battery_slots[slot].dac_value+1);
  138. #endif
  139. set_dac(slot, ++battery_slots[slot].dac_value);
  140. }
  141. else {
  142. // we want to give more current, but we can't ?!
  143. #ifdef DEBUG_CTRL
  144. printf("Ctrl: Unable to increase DAC to %d\n", battery_slots[slot].dac_value+1);
  145. #endif
  146. *battery_slots[slot].state = SLOT_WARN_HIGHER_DAC_NOT_POSSIBLE;
  147. }
  148. }
  149. // no else statement here: we are ok, since we are in the tolerance measure
  150. } else if (battery_slots[slot].set_current < 0) {
  151. // negative current -> discharge (with PWM)
  152. if (battery_slots[slot].dac_value != 0) {
  153. // seems like we switched from a charging before
  154. // -> disable DAC before getting active
  155. #ifdef DEBUG_CTRL
  156. printf("Detected active dac. Value: %d. Disabling.", battery_slots[slot].dac_value);
  157. #endif
  158. set_dac(slot, 0);
  159. }
  160. if (battery_slots[slot].set_current + BATTERY_CURRENT_THRESHOLD > battery_slots[slot].measurement.current) {
  161. // we are outside of the tolerance band
  162. // exceeded to the upper limit
  163. // -> update pwm value, decrease the voltage
  164. // @todo debugging & validation: ensure that this directive works
  165. #ifdef DEBUG_CTRL
  166. printf("timer count: %d\n", DL_Timer_getTimerCount(battery_slots[0].timer));
  167. #endif
  168. if (battery_slots[slot].pwm_value+1 <= DL_Timer_getTimerCount(battery_slots[0].timer)) {
  169. // pwm is inverse to the DAC since dragging more current means more negative
  170. #ifdef DEBUG_TRACE_CTRL
  171. printf("Ctrl: Increasing PWM to %d\n", battery_slots[slot].pwm_value+1);
  172. #endif
  173. set_pwm(slot, ++battery_slots[slot].pwm_value);
  174. }
  175. else {
  176. // we want to give more current, but we can't ?!
  177. #ifdef DEBUG_CTRL
  178. printf("Ctrl: Unable to increase PWM to %d\n", battery_slots[slot].pwm_value+1);
  179. #endif
  180. *battery_slots[slot].state = SLOT_WARN_HIGHER_PWM_NOT_POSSIBLE;
  181. }
  182. } else if (battery_slots[slot].set_current - BATTERY_CURRENT_THRESHOLD < battery_slots[slot].measurement.current) {
  183. // we are outside of the tolerance band
  184. // exceeded to the upplowerer limit
  185. // -> update pwm value, increase the voltage
  186. if (battery_slots[slot].pwm_value-1 >= 0) {
  187. #ifdef DEBUG_TRACE_CTRL
  188. printf("Ctrl: Decreasing PWM to %d\n", battery_slots[slot].pwm_value-1);
  189. #endif
  190. set_pwm(slot, --battery_slots[slot].pwm_value);
  191. }
  192. else {
  193. // we want to give more current, but we can't ?!
  194. #ifdef DEBUG_CTRL
  195. printf("Ctrl: Unable to decrease PWM to %d\n", battery_slots[slot].pwm_value-1);
  196. #endif
  197. *battery_slots[slot].state = SLOT_WARN_LOWER_PWM_NOT_POSSIBLE;
  198. }
  199. }
  200. } else {
  201. // we have 0 -> stop charging and discharging
  202. #ifdef DEBUG_CTRL
  203. printf("0 current -> Disabling slot.\n");
  204. #endif
  205. batteryslots_disable(slot);
  206. }
  207. }
  208. BatterySlotManager battery_slotmgr = {
  209. .init = batteryslots_init,
  210. .read_state = batteryslots_read_state,
  211. .adjust_current = batteryslots_adjust_current,
  212. .disable = batteryslots_disable
  213. };