Ver Fonte

adc hal layer implementation

namrota ghosh há 8 meses atrás
pai
commit
4159971ed1
4 ficheiros alterados com 299 adições e 98 exclusões
  1. 32 44
      src/adc.c
  2. 0 48
      src/adc.h
  3. 235 0
      src/hal/adc_hal.c
  4. 32 6
      src/interfaces/adc_interface.h

+ 32 - 44
src/adc.c

@@ -3,11 +3,15 @@
 #include "ti/driverlib/dl_i2c.h"
 #include "ti/driverlib/m0p/dl_core.h"
 #include "ti_msp_dl_config.h"
-#include "adc.h"
+#include "src/interfaces/adc.h"
 #include <stdint.h>
 #include <stdio.h>
+#include "src/interfaces/adc_interface.h"
 
-volatile bool gRxComplete;
+
+static ADC_Params adc_params;
+static ADC_MeasurementState adc_state = ADC_STATE_CONFIGURE;
+/*volatile bool gRxComplete;
 volatile bool gTxComplete;
 uint8_t gTxPacket[I2C_TX_MAX_PACKET_SIZE];
 uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
@@ -15,15 +19,15 @@ uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
 uint32_t gTxADClen, gTxADCcount;
 uint32_t gRxADClen, gRxADCcount;
 
-static ADC_PARAMS adc_params;
-static ADC_MeasurementState adc_state = ADC_STATE_CONFIGURE;
+*/
 
 /*
 * Creating Configuartion Register as mentioned in the datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/22226a.pdf
 * Under section 5.2 Configuration Register
 * The function is private to ADC and is can only be called in this file
 */
-static uint8_t ADC_ConstructConfigBytes(ADC_PARAMS params) {
+
+/*static uint8_t ADC_ConstructConfigBytes(ADC_PARAMS params) {
 
   uint8_t config = 0;
 
@@ -63,11 +67,12 @@ static uint8_t ADC_ConstructConfigBytes(ADC_PARAMS params) {
 
   return config;
 }
+*/
 
 /* Tansmit Data from MCU to ADC:  Function to SET configuration to ADC over
  * I2C*/
 
-void ADC_SetConfigurationBytes(ADC_PARAMS params) {
+/*void ADC_SetConfigurationBytes(ADC_PARAMS params) {
   // **Construct Configuration Byte**
   uint8_t config_byte = ADC_ConstructConfigBytes(params);
 
@@ -98,7 +103,7 @@ void ADC_SetConfigurationBytes(ADC_PARAMS params) {
   //  ;
   // printf("Configuration Sent Successfully for 0x%X!\n", config_byte);
 }
-
+*/
 /*
 READY BIT:
     This bit is the data ready flag. In read mode, this bit indicates if the
@@ -110,7 +115,7 @@ Conversion mode, writing this bit to “1” initiates a new conversion.
 
 */
 
-bool ADC_CheckReadyBit(uint8_t slot_id, ADC_PARAMS params) {
+/*bool ADC_CheckReadyBit(uint8_t slot_id, ADC_PARAMS params) {
   // Buffer for ADC data (MSB, LSB, Config Byte)
   uint8_t adc_data[3];
   uint8_t adc_address = ADC_TARGET_BASE_ADDRESS + slot_id;
@@ -139,7 +144,7 @@ bool ADC_CheckReadyBit(uint8_t slot_id, ADC_PARAMS params) {
   //       config_adc_byte, ready);
   return ready;
 }
-
+*/
 /*
 Function to read ADC DATA: This function simply retrieves the ADC raw digital
 output as 16-bit signed integer.
@@ -186,7 +191,7 @@ D15.
 
 */
 
-int16_t ADC_ReadData(uint8_t slot_id, ADC_PARAMS params) {
+/*int16_t ADC_ReadData(uint8_t slot_id, ADC_PARAMS params) {
 
   // Buffer for ADC data (MSB, LSB, Config Byte)
   uint8_t adc_data[3] = {0};
@@ -242,17 +247,18 @@ int16_t ADC_ReadData(uint8_t slot_id, ADC_PARAMS params) {
   //printf("Raw ADC Value: 0x%0X (%d)\n", raw_adc, raw_adc);
   return raw_adc;
 }
-
+*/
 // **Interrupt handler for ADC Read Completion**
-void I2C_ADC_IRQHandler(void) {
+/*void I2C_ADC_IRQHandler(void) {
   if (DL_I2C_getPendingInterrupt(I2C_controller_INST) ==
       DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER) {
     gRxComplete = true;
   }
 }
+*/
 
 /* Function to Convert ADC Reading to Voltage */
-uint16_t ADC_ConvertToVoltage(int16_t adc_value, ADC_PARAMS params) {
+/*uint16_t ADC_ConvertToVoltage(int16_t adc_value, ADC_PARAMS params) {
   uint16_t measured_voltage = 0;
   uint16_t LSB = 0;
   uint32_t max_adc_value = 1;
@@ -274,9 +280,9 @@ uint16_t ADC_ConvertToVoltage(int16_t adc_value, ADC_PARAMS params) {
   measured_voltage = (((uint32_t)adc_value) * 2.7);
   return (uint16_t)measured_voltage;
 }
-
+*/
 /* Function to Convert ADC Reading to Voltage */
-uint16_t ADC_ConvertToCurrent(int16_t adc_value, ADC_PARAMS params) {
+/*uint16_t ADC_ConvertToCurrent(int16_t adc_value, ADC_PARAMS params) {
   int16_t current_mA = 0;
   // uint8_t r_shunt= 0.1;
   // Convert ADC value to voltage across shunt resistor:
@@ -287,8 +293,10 @@ uint16_t ADC_ConvertToCurrent(int16_t adc_value, ADC_PARAMS params) {
   // printf("Converted Current: %d mA.\n", current_mA);
   return (int16_t)current_mA;
 }
+*/
 
-void Battery_UpdateADCReading(uint8_t slot, uint8_t channel) {
+void updateADCReading_multichannel(uint8_t slot, uint8_t channel) {
+  
   while (adc_state != ADC_STATE_DONE) {
     switch (adc_state) {
 
@@ -298,12 +306,14 @@ void Battery_UpdateADCReading(uint8_t slot, uint8_t channel) {
       adc_params.resolution = 12;
       adc_params.continuous = 0;
       adc_params.gain = 1;
-      ADC_SetConfigurationBytes(adc_params);
+      adc_hal.configure(slot, adc_params);
+      //ADC_SetConfigurationBytes(adc_params);
       adc_state = ADC_STATE_WAIT;
       break;
 
     case ADC_STATE_WAIT:
-      if (ADC_CheckReadyBit(slot, adc_params)) {
+      //if (ADC_CheckReadyBit(slot, adc_params)) {
+      if(adc_hal.is_ready(slot, adc_params)){
         adc_state = ADC_STATE_READ;
       }
       break;
@@ -311,18 +321,18 @@ void Battery_UpdateADCReading(uint8_t slot, uint8_t channel) {
     case ADC_STATE_READ:
       if (channel == 0) {
 
-        int16_t raw_adc_voltage = ADC_ReadData(slot, adc_params);
+        int16_t raw_adc_voltage = adc_hal.read_raw(slot, adc_params);
         batteries[slot].voltage =
-            ADC_ConvertToVoltage(raw_adc_voltage, adc_params); // Double check voltage dividers later on to get the right value (possibly instead of 2)
+            adc_hal.convert_voltage(raw_adc_voltage, adc_params); 
         printf("[ADC] Battery voltage for slot %d is %u mV.\n", slot,
                batteries[slot].voltage);
         adc_state = ADC_STATE_DONE;
 
       } else if (channel == 1) {
 
-        int16_t raw_adc_current = ADC_ReadData(slot, adc_params);
+        int16_t raw_adc_current = adc_hal.read_raw(slot, adc_params);
         batteries[slot].current =
-            ADC_ConvertToCurrent(raw_adc_current, adc_params);
+            adc_hal.convert_current(raw_adc_current, adc_params);
         printf("[ADC] Battery current for slot %d is %u mA.\n", slot,
                batteries[slot].current);
 
@@ -338,25 +348,3 @@ void Battery_UpdateADCReading(uint8_t slot, uint8_t channel) {
 
   adc_state = ADC_STATE_CONFIGURE;
 }
-
-void Battery_ReadState(uint8_t slot_id) {
-
-  uint16_t voltage_mv = batteries[slot_id].voltage;
-  uint16_t min_voltage = batteries[slot_id].min_voltage;
-  uint16_t max_voltage = batteries[slot_id].max_voltage;
-
-  /* Changing the battery states based on adc values*/
-  if (voltage_mv < 500) {
-    batteries[slot_id].state = STATE_EMPTY;
-  } else if (voltage_mv >= 500 && voltage_mv < 3000) { //TODO: change the voltage_mv to min_voltage
-    batteries[slot_id].state = STATE_BATTERY_DETECTED;
-  } else if (voltage_mv >= 3000 && voltage_mv < 4200) {
-    batteries[slot_id].state = STATE_MEASUREMENT_IN_PROGRESS;
-  }
-  // once MCU is done reading ADC:
-  else if (adc_state== ADC_STATE_DONE && batteries[slot_id].state== STATE_MEASUREMENT_IN_PROGRESS) {
-    batteries[slot_id].state = STATE_MEASUREMENT_DONE;
-  } else {
-    batteries[slot_id].state = STATE_OVERCHARGING;
-  }
-}

+ 0 - 48
src/adc.h

@@ -1,48 +0,0 @@
-
-#ifndef ADC_H_
-#include "ti/driverlib/dl_i2c.h"
-#include "ti_msp_dl_config.h"
-#include "battery.h"
-#define ADC_TARGET_BASE_ADDRESS (0x68)
-#define ADC_VREF_MV (2048)
-#define DELAY (100000) //define timeout limit
-#define ADC_CHANNEL_NUM (2)
-
-//Maximum packet sizes
-#define I2C_TX_MAX_PACKET_SIZE (16)
-#define I2C_RX_MAX_PACKET_SIZE (16)
-
-//Flag for READ and WRITE
-extern volatile bool gRxComplete;
-extern volatile bool gTxComplete;
-extern uint8_t gTxPacket[I2C_TX_MAX_PACKET_SIZE];
-extern uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
-
-/*Counters for Tx and Rx length and bytes sent*/
-extern uint32_t gTxADClen, gTxADCcount;
-extern uint32_t gRxADClen, gRxADCcount;
-
-//ADC states
-typedef enum{
-    ADC_STATE_CONFIGURE,
-    ADC_STATE_WAIT,
-    ADC_STATE_READ,
-    ADC_STATE_DONE
-}ADC_MeasurementState;
-
-typedef struct{
-    uint8_t channel;
-    uint8_t resolution;
-    bool continuous;
-    uint8_t gain;
-} ADC_PARAMS;
-
-
-void ADC_SetConfigurationBytes(ADC_PARAMS params);
-bool ADC_CheckReadyBit(uint8_t slot_id, ADC_PARAMS params);
-int16_t ADC_ReadData(uint8_t slot_id, ADC_PARAMS params);
-void Battery_UpdateVoltage(ADC_PARAMS params);
-void Battery_UpdateCurrent(ADC_PARAMS params);
-void Battery_UpdateADCReading(uint8_t slot, uint8_t channel);
-void Battery_ReadState(uint8_t slot_id);
-#endif

+ 235 - 0
src/hal/adc_hal.c

@@ -0,0 +1,235 @@
+/*
+This adc file will handle:
+- packet configuration: bool
+- adc ready flag: bool
+- adc_read_raw: int_16
+
+Function to read ADC DATA: This function simply retrieves the ADC raw digital
+output as 16-bit signed integer.
+* The value returned is not yet converted to VOLTAGE.
+* adc_data[2]: Buffer with an array of size 2, to store two bytes of ADC data.
+*             Since, the ADC output consists of two 8-bit bytes:
+*             - adc_data[0] (MSB)
+*             - adc_data[1] (LSB)
+* Next, we verify if the the I2C bus is busy using the function in the
+driverlib: DL_I2C_get ControllerStatus
+*       - Prevents collisions by ensuring no two I2C device is using the same
+bus before initializing.
+* BEGIN I2C READ operation to request 2 bytes from the ADC->
+DL_I2C_startControllerTransfer()
+* Parameters:
+    - I2C_controller_INST: Refererence to the I2C instance bring used.
+    - DEF_TARGET_ADDR_ADC: Address of the ADC
+    - DL_I2C_CONTROLLER_DIRECTION_RX: Indicates that we want to receive the data
+from ADC
+    - 2: Specifies that we expect 2 bytes from the ADC
+* WAIT for the data to be received: (DL_I2C_getControllerStatus())
+    - Waits until the ADC sends the data over I2C.
+    - Ensures that the data transfer is complete before proceeding.
+* READ the two bytes of ADc Data:
+    - adc_data[0] : MSB
+    - adc_data[1] : LSB
+    - adc_data[2] : config_bytes
+    ADC sends its 16-bit value in two parts over 8-bit I2C data frames.
+* COMBINE the two bytes into 16-bit Integer:
+    - Shifts the MSB(first byte) left by 8 bits to make space for LSB.
+    - Performs a bitwise OR operation to combine MSB and LSB into a single
+16-bit number.
+* PRINT HEXADEC and DECIMAL format for DEBUGGING.
+* Output code is in binary and is proportional to the Input Voltage and PGA
+settings.
+* From the datasheet:
+https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/22226a.pdf
+    - Equation 4.4 is being used to convert the output codes to input voltage
+
+* The ACK bits after conversion is issued by the Master, when the device
+receives a READ command, it outputs two data bytes followed by a configuration
+register in 16 bit conversion mode the MSB(=sign bit) of the first data type is
+D15.
+*/
+
+
+#include "src/interfaces/i2c_controller_interface.h"
+#include "src/interfaces/adc_interface.h"
+#include "ti_msp_dl_config.h"
+#include <stdio.h>
+
+volatile bool gRxComplete;
+volatile bool gTxComplete;
+uint8_t gTxPacket[I2C_TX_MAX_PACKET_SIZE];
+uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
+uint32_t gTxADClen, gTxADCcount;
+uint32_t gRxADClen, gRxADCcount;
+
+/*
+* Creating Configuartion Register as mentioned in the datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/22226a.pdf
+* Under section 5.2 Configuration Register
+* The function is private to ADC and is can only be called in this file
+*/
+static uint8_t construct_config_byte(ADC_Params params) {
+
+  uint8_t config = 0;
+
+  config |= ((params.channel) << 5); // Channel Selection (Bits 6-5)
+
+  config |= (1 << 4); // One-Shot Mode
+
+  switch (params.resolution) {
+  case 12:
+    config |= (0b00 << 2);
+    break;
+  case 14:
+    config |= (0b01 << 2);
+    break;
+  case 16:
+    config |= (0b10 << 2);
+    break;
+  default:
+    printf("ERROR: Invalid Resolution!\n");
+    return 0;
+  }
+
+  switch (params.gain) {
+  case 1:
+    config |= (0b00);
+    break;
+  case 2:
+    config |= (0b01);
+    break;
+  case 4:
+    config |= (0b10);
+    break;
+  default:
+    printf("ERROR: Invalid Gain!\n");
+    return 0;
+  }
+
+  return config;
+}
+
+/* Tansmit Data from MCU to ADC:  Function to SET configuration to ADC over
+ * I2C*/
+
+static bool adc_configure(uint8_t slot_id, ADC_Params params) {
+  // **Construct Configuration Byte**
+  uint8_t config_byte = construct_config_byte(params);
+  // Wait for I2C Bus to be Free**
+  while (DL_I2C_getControllerStatus(I2C_controller_INST) &
+         DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
+    ;
+  if(config_byte == 0xFF) return false;
+  // Prepare TX Buffer:
+  gTxPacket[0] = config_byte;
+  gTxADClen = 1;
+  gTxADCcount = 0;
+  gTxComplete = false;
+
+  return i2c_hal.write(ADC_TARGET_BASE_ADDRESS, gTxPacket, gTxADClen);
+  //return i2c_hal.write(ADC_TARGET_BASE_ADDRESS + slot_id, &config_byte, 1);
+}
+
+
+/*
+READY BIT:
+    This bit is the data ready flag. In read mode, this bit indicates if the
+output register has been updated with a latest conversion result. In One-Shot
+Conversion mode, writing this bit to “1” initiates a new conversion.
+
+    1= Output Register has not been updated
+    0= Output Register has been updated
+
+*/
+
+static bool adc_is_ready(uint8_t slot_id, ADC_Params params) {
+  uint8_t adc_address = ADC_TARGET_BASE_ADDRESS + slot_id;
+  gRxADClen = 3;
+  gRxADCcount = 0;
+  gRxComplete = false;
+
+  i2c_hal.read(adc_address, gRxADClen);
+  //printf("Reading ADC Data from MCP3428 for channel: %u\n", params.channel);
+  //i2c_hal.read(ADC_TARGET_BASE_ADDRESS + slot_id, 3);
+  uint8_t config_adc_byte = gRxPacket[2];
+  // Ready bit is bit 7
+  bool ready = (config_adc_byte & 0x80) == 0;
+  return ready;
+}
+
+
+static int16_t read_adc_raw_data(uint8_t slot_id, ADC_Params params) {
+
+  // Buffer for ADC data (MSB, LSB, Config Byte)
+  uint8_t adc_data[3] = {0};
+  int16_t raw_adc = 0;
+  uint8_t bytes_to_read = 3;
+  gRxADClen = bytes_to_read;
+  gRxADCcount = 0;
+  gRxComplete = false;
+  i2c_hal.read(ADC_TARGET_BASE_ADDRESS + slot_id, gRxADClen);
+  uint8_t msb = gRxPacket[0];
+  uint8_t lsb = gRxPacket[1];
+  uint8_t config_adc_byte = gRxPacket[2];
+  //printf("MSB: 0x%02X, LSB: 0x%02X\n", msb, lsb);
+  uint8_t gain_setting = (config_adc_byte & 0x03);
+  uint8_t gain_multiplier = (1 << gain_setting); // Gain values: 1, 2, 4, 8
+  if (params.resolution == 16) {
+    raw_adc = (msb << 8) | lsb;
+    if (raw_adc > 32767)
+      raw_adc -= 65536;
+  } else if (params.resolution == 14) {
+    raw_adc = ((msb & 0b00111111) << 8) | lsb;
+    if (raw_adc > 8191)
+      raw_adc -= 16384;
+  } else if (params.resolution == 12) {
+    raw_adc = ((msb & 0b00001111) << 8) | lsb;
+    if (raw_adc > 2047)
+      raw_adc -= 4096;
+  }
+  //printf("Raw ADC Value: 0x%0X (%d)\n", raw_adc, raw_adc);
+  return raw_adc;
+}
+
+/* Function to Convert ADC Reading to Voltage */
+static uint16_t adc_voltage(int16_t adc_value, ADC_Params params) {
+  uint16_t measured_voltage = 0;
+  uint16_t LSB = 0;
+  uint32_t max_adc_value = 1;
+
+  switch (params.resolution) {
+  case 12: // 12-bit
+    max_adc_value = 4095;
+    break;
+  case 14: // 14-bit
+    max_adc_value = 16383;
+    break;
+  case 16: // 16-bit
+    max_adc_value = 65535;
+    break;
+  default:
+    printf("Error: Unknown ADC Resolution!\n");
+    return 0;
+  }
+  measured_voltage = (((uint32_t)adc_value) * 2.7);
+  return (uint16_t)measured_voltage;
+}
+
+/* Function to Convert ADC Reading to Voltage */
+uint16_t adc_current(int16_t adc_value, ADC_Params params) {
+  int16_t current_mA = 0;
+  // Convert ADC value to voltage across shunt resistor:
+  uint16_t voltage_mV = adc_voltage(adc_value, params);
+  uint8_t gain_multiplier = (1 << (params.gain - 1));
+  // Convert voltage drop across shunt resistor to current
+  current_mA = (adc_value) * (10 * gain_multiplier);
+  // printf("Converted Current: %d mA.\n", current_mA);
+  return (int16_t)current_mA;
+}
+
+
+ADC_Interface adc_hal= {
+    .configure= adc_configure,
+    .read_raw= read_adc_raw_data,
+    .is_ready= adc_is_ready,
+    .convert_voltage= adc_voltage,
+    .convert_current= adc_current
+};

+ 32 - 6
src/interfaces/adc_interface.h

@@ -1,17 +1,43 @@
 #ifndef ADC_INTERFACE_H_
 #define ADC_INTERFACE_H_
+
 #include <stdint.h>
 #include <stdbool.h>
-#include "ti/driverlib/dl_i2c.h"
-#include "ti_msp_dl_config.h"
 
-//Default ADC ADDRESS:
-//#define ADC_TARGET_BASE_ADDRESS (0x68)
-//ADC  VREF in millivolts
-//#define ADC_VREF_MV (2048)
+#define ADC_TARGET_BASE_ADDRESS (0x68)
+#define ADC_VREF_MV (2048)
+#define DELAY (100000) //define timeout limit
+#define ADC_CHANNEL_NUM (2)
+//Maximum packet sizes
+#define I2C_TX_MAX_PACKET_SIZE (16)
+#define I2C_RX_MAX_PACKET_SIZE (16)
+
+//Flag for READ and WRITE
+extern volatile bool gRxComplete;
+extern volatile bool gTxComplete;
+extern uint8_t gTxPacket[I2C_TX_MAX_PACKET_SIZE];
+extern uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
+
+/*Counters for Tx and Rx length and bytes sent*/
+extern uint32_t gTxADClen, gTxADCcount;
+extern uint32_t gRxADClen, gRxADCcount;
 
+typedef struct {
+    uint8_t channel;
+    uint8_t resolution;
+    bool continuous;
+    uint8_t gain;
+} ADC_Params;
 
 
+typedef struct {
+    bool (*configure)(uint8_t slot_id, ADC_Params params);
+    bool (*is_ready)(uint8_t slot_id, ADC_Params params);
+    int16_t (*read_raw)(uint8_t slot_id, ADC_Params params);
+    uint16_t (*convert_voltage)(int16_t raw, ADC_Params params);
+    uint16_t (*convert_current)(int16_t raw, ADC_Params params);
+} ADC_Interface;
 
+extern ADC_Interface adc_hal;
 
 #endif