|
|
@@ -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/peripherals/adc/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
|
|
|
+};
|