adc.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #include "battery.h"
  2. #include "ti/driverlib/dl_i2c.h"
  3. #include "ti/driverlib/m0p/dl_core.h"
  4. #include "ti_msp_dl_config.h"
  5. #include <adc.h>
  6. #include <stdint.h>
  7. #include <stdio.h>
  8. volatile bool gRxComplete;
  9. volatile bool gTxComplete;
  10. uint8_t gTxPacket[I2C_TX_MAX_PACKET_SIZE];
  11. uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
  12. uint32_t gTxADClen, gTxADCcount;
  13. uint32_t gRxADClen, gRxADCcount;
  14. static ADC_PARAMS adc_params;
  15. static ADC_MeasurementState adc_state = ADC_STATE_CONFIGURE;
  16. uint8_t ADC_ConstructConfigBytes(ADC_PARAMS params) {
  17. uint8_t config = 0;
  18. config |= ((params.channel) << 5); // Channel Selection (Bits 6-5)
  19. config |= (1 << 4); // One-Shot Mode
  20. switch (params.resolution) {
  21. case 12:
  22. config |= (0b00 << 2);
  23. break;
  24. case 14:
  25. config |= (0b01 << 2);
  26. break;
  27. case 16:
  28. config |= (0b10 << 2);
  29. break;
  30. default:
  31. printf("ERROR: Invalid Resolution!\n");
  32. return 0;
  33. }
  34. switch (params.gain) {
  35. case 1:
  36. config |= (0b00);
  37. break;
  38. case 2:
  39. config |= (0b01);
  40. break;
  41. case 4:
  42. config |= (0b10);
  43. break;
  44. default:
  45. printf("ERROR: Invalid Gain!\n");
  46. return 0;
  47. }
  48. return config;
  49. }
  50. /* Tansmit Data from MCU to ADC: Function to SET configuration to ADC over
  51. * I2C*/
  52. void ADC_SetConfigurationBytes(ADC_PARAMS params) {
  53. // **Construct Configuration Byte**
  54. uint8_t config_byte = ADC_ConstructConfigBytes(params);
  55. // Wait for I2C Bus to be Free**
  56. while (DL_I2C_getControllerStatus(I2C_controller_INST) &
  57. DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
  58. ;
  59. // **Start I2C Write Transaction**
  60. // Prepare TX Buffer:
  61. gTxPacket[0] = config_byte;
  62. gTxADClen = 1;
  63. gTxADCcount = 0;
  64. gTxComplete = false;
  65. DL_I2C_flushControllerTXFIFO(I2C_controller_INST);
  66. DL_I2C_fillControllerTXFIFO(I2C_controller_INST, (uint8_t*) &gTxPacket, 1);
  67. DL_I2C_startControllerTransfer(I2C_controller_INST, ADC_TARGET_BASE_ADDRESS,
  68. DL_I2C_CONTROLLER_DIRECTION_TX, gTxADClen);
  69. //DL_I2C_enableInterrupt(I2C_controller_INST,
  70. // DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);
  71. // while(!gTxComplete);
  72. // **Ensure STOP Condition is Sent**
  73. while (DL_I2C_getControllerStatus(I2C_controller_INST) &
  74. DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
  75. ;
  76. // printf("Configuration Sent Successfully for 0x%X!\n", config_byte);
  77. }
  78. /*
  79. READY BIT:
  80. This bit is the data ready flag. In read mode, this bit indicates if the
  81. output register has been updated with a latest conversion result. In One-Shot
  82. Conversion mode, writing this bit to “1” initiates a new conversion.
  83. 1= Output Register has not been updated
  84. 0= Output Register has been updated
  85. */
  86. bool ADC_CheckReadyBit(uint8_t slot_id, ADC_PARAMS params) {
  87. // Buffer for ADC data (MSB, LSB, Config Byte)
  88. uint8_t adc_data[3];
  89. uint8_t adc_address = ADC_TARGET_BASE_ADDRESS + slot_id;
  90. printf("Reading ADC Data from MCP3428 for channel: %u\n", params.channel);
  91. gRxADClen = 3;
  92. gRxADCcount = 0;
  93. gRxComplete = false;
  94. DL_I2C_startControllerTransfer(I2C_controller_INST, adc_address,
  95. DL_I2C_CONTROLLER_DIRECTION_RX, gRxADClen);
  96. DL_I2C_enableInterrupt(I2C_controller_INST,
  97. DL_I2C_INTERRUPT_CONTROLLER_RXFIFO_TRIGGER);
  98. while (DL_I2C_getControllerStatus(I2C_controller_INST) &
  99. DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
  100. ;
  101. while (!gRxComplete)
  102. ;
  103. uint8_t config_adc_byte = gRxPacket[2];
  104. // Ready bit is bit 7
  105. bool ready = (config_adc_byte & 0x80) == 0;
  106. printf("Slot: %d | Config Byte: 0x%02X | READY Bit: %d\n", slot_id,
  107. config_adc_byte, ready);
  108. return ready;
  109. }
  110. /*
  111. Function to read ADC DATA: This function simply retrieves the ADC raw digital
  112. output as 16-bit signed integer.
  113. * The value returned is not yet converted to VOLTAGE.
  114. * adc_data[2]: Buffer with an array of size 2, to store two bytes of ADC data.
  115. * Since, the ADC output consists of two 8-bit bytes:
  116. * - adc_data[0] (MSB)
  117. * - adc_data[1] (LSB)
  118. * Next, we verify if the the I2C bus is busy using the function in the
  119. driverlib: DL_I2C_get ControllerStatus
  120. * - Prevents collisions by ensuring no two I2C device is using the same
  121. bus before initializing.
  122. * BEGIN I2C READ operation to request 2 bytes from the ADC->
  123. DL_I2C_startControllerTransfer()
  124. * Parameters:
  125. - I2C_controller_INST: Refererence to the I2C instance bring used.
  126. - DEF_TARGET_ADDR_ADC: Address of the ADC
  127. - DL_I2C_CONTROLLER_DIRECTION_RX: Indicates that we want to receive the data
  128. from ADC
  129. - 2: Specifies that we expect 2 bytes from the ADC
  130. * WAIT for the data to be received: (DL_I2C_getControllerStatus())
  131. - Waits until the ADC sends the data over I2C.
  132. - Ensures that the data transfer is complete before proceeding.
  133. * READ the two bytes of ADc Data:
  134. - adc_data[0] : MSB
  135. - adc_data[1] : LSB
  136. - adc_data[2] : config_bytes
  137. ADC sends its 16-bit value in two parts over 8-bit I2C data frames.
  138. * COMBINE the two bytes into 16-bit Integer:
  139. - Shifts the MSB(first byte) left by 8 bits to make space for LSB.
  140. - Performs a bitwise OR operation to combine MSB and LSB into a single
  141. 16-bit number.
  142. * PRINT HEXADEC and DECIMAL format for DEBUGGING.
  143. * Output code is in binary and is proportional to the Input Voltage and PGA
  144. settings.
  145. * From the datasheet:
  146. https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/22226a.pdf
  147. - Equation 4.4 is being used to convert the output codes to input voltage
  148. * The ACK bits after conversion is issued by the Master, when the device
  149. receives a READ command, it outputs two data bytes followed by a configuration
  150. register in 16 bit conversion mode the MSB(=sign bit) of the first data type is
  151. D15.
  152. */
  153. int16_t ADC_ReadData(uint8_t slot_id, ADC_PARAMS params) {
  154. // Buffer for ADC data (MSB, LSB, Config Byte)
  155. uint8_t adc_data[3] = {0};
  156. int16_t raw_adc = 0;
  157. uint8_t bytes_to_read = 3;
  158. uint8_t adc_address = ADC_TARGET_BASE_ADDRESS + slot_id;
  159. // printf("Reading ADC Data from MCP3428 for channel: %u\n", params.channel);
  160. if (DL_I2C_getControllerStatus(I2C_controller_INST) &
  161. DL_I2C_CONTROLLER_STATUS_BUSY_BUS) {
  162. printf("Error: I2C bus stuck! Resetting..\n");
  163. // DL_I2C_resetControllerTransfer(I2C_controller_INST);
  164. }
  165. gRxADClen = bytes_to_read;
  166. gRxADCcount = 0;
  167. gRxComplete = false;
  168. DL_I2C_startControllerTransfer(I2C_controller_INST, adc_address,
  169. DL_I2C_CONTROLLER_DIRECTION_RX, gRxADClen);
  170. DL_I2C_enableInterrupt(I2C_controller_INST,
  171. DL_I2C_INTERRUPT_CONTROLLER_RXFIFO_TRIGGER);
  172. while (DL_I2C_getControllerStatus(I2C_controller_INST) &
  173. DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
  174. ;
  175. while (!gRxComplete)
  176. ;
  177. uint8_t msb = gRxPacket[0];
  178. uint8_t lsb = gRxPacket[1];
  179. uint8_t config_adc_byte = gRxPacket[2];
  180. uint8_t gain_setting = (config_adc_byte & 0x03);
  181. uint8_t gain_multiplier = (1 << gain_setting); // Gain values: 1, 2, 4, 8
  182. if (params.resolution == 16) {
  183. raw_adc = (msb << 8) | lsb;
  184. if (raw_adc > 32767)
  185. raw_adc -= 65536;
  186. } else if (params.resolution == 14) {
  187. raw_adc = ((msb & 0b00111111) << 8) | lsb;
  188. if (raw_adc > 8191)
  189. raw_adc -= 16384;
  190. } else if (params.resolution == 12) {
  191. raw_adc = ((msb & 0b00001111) << 8) | lsb;
  192. if (raw_adc > 2047)
  193. raw_adc -= 4096;
  194. }
  195. printf("Raw ADC Value: 0x%0X (%d)\n", raw_adc, raw_adc);
  196. return raw_adc;
  197. }
  198. // **Interrupt handler for ADC Read Completion**
  199. void I2C_ADC_IRQHandler(void) {
  200. if (DL_I2C_getPendingInterrupt(I2C_controller_INST) ==
  201. DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER) {
  202. gRxComplete = true;
  203. }
  204. }
  205. /* Function to Convert ADC Reading to Voltage */
  206. uint16_t ADC_ConvertToVoltage(int16_t adc_value, ADC_PARAMS params) {
  207. uint16_t measured_voltage = 0;
  208. uint16_t LSB = 0;
  209. uint32_t max_adc_value = 1;
  210. switch (params.resolution) {
  211. case 12: // 12-bit
  212. max_adc_value = 4095;
  213. break;
  214. case 14: // 14-bit
  215. max_adc_value = 16383;
  216. break;
  217. case 16: // 16-bit
  218. max_adc_value = 65535;
  219. break;
  220. default:
  221. printf("Error: Unknown ADC Resolution!\n");
  222. return 0;
  223. }
  224. measured_voltage = (((uint32_t)adc_value * ADC_VREF_MV) * 3) / max_adc_value;
  225. return (uint16_t)measured_voltage;
  226. }
  227. /* Function to Convert ADC Reading to Voltage */
  228. uint16_t ADC_ConvertToCurrent(int16_t adc_value, ADC_PARAMS params) {
  229. int16_t current_mA = 0;
  230. // uint8_t r_shunt= 0.1;
  231. // Convert ADC value to voltage across shunt resistor:
  232. uint16_t voltage_mV = ADC_ConvertToVoltage(adc_value, params);
  233. uint8_t gain_multiplier = (1 << (params.gain - 1));
  234. // Convert voltage drop across shunt resistor to current
  235. current_mA = (voltage_mV) / (0.1 * gain_multiplier);
  236. // printf("Converted Current: %d mA.\n", current_mA);
  237. return (int16_t)current_mA;
  238. }
  239. void Battery_UpdateADCReading(uint8_t slot, uint8_t channel) {
  240. while (adc_state != ADC_STATE_DONE) {
  241. switch (adc_state) {
  242. case ADC_STATE_CONFIGURE:
  243. adc_params.channel = channel;
  244. // printf("Channel: %d\n", adc_params.channel);
  245. adc_params.resolution = 12;
  246. adc_params.continuous = 1;
  247. adc_params.gain = 1;
  248. ADC_SetConfigurationBytes(adc_params);
  249. adc_state = ADC_STATE_WAIT;
  250. break;
  251. case ADC_STATE_WAIT:
  252. if (ADC_CheckReadyBit(slot, adc_params)) {
  253. adc_state = ADC_STATE_READ;
  254. }
  255. break;
  256. case ADC_STATE_READ:
  257. if (channel == 0) {
  258. int16_t raw_adc_voltage = ADC_ReadData(slot, adc_params);
  259. batteries[slot].voltage =
  260. ADC_ConvertToVoltage(raw_adc_voltage, adc_params);
  261. printf("Battery voltage for slot %d is %u mV.\n", slot,
  262. batteries[slot].voltage);
  263. adc_state = ADC_STATE_DONE;
  264. } else if (channel == 1) {
  265. int16_t raw_adc_current = ADC_ReadData(slot, adc_params);
  266. batteries[slot].current =
  267. ADC_ConvertToCurrent(raw_adc_current, adc_params);
  268. printf("Battery current for slot %d is %u mA.\n", slot,
  269. batteries[slot].current);
  270. adc_state = ADC_STATE_DONE;
  271. }
  272. break;
  273. default:
  274. channel = 0;
  275. adc_state = ADC_STATE_CONFIGURE;
  276. break;
  277. }
  278. }
  279. adc_state = ADC_STATE_CONFIGURE;
  280. }