adc.c 10 KB

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