#include #include #include #include "battery.h" #include "ti/driverlib/dl_i2c.h" #include "ti/driverlib/m0p/dl_core.h" #include "ti_msp_dl_config.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; uint8_t ADC_ConstructConfigBytes(ADC_PARAMS params) { uint8_t config = 0; config |= ((params.channel - 1) << 5); // Channel Selection (Bits 6-5) if (params.continuous) config |= (1 << 4); // Continuous Mode (Bit 4) 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*/ void ADC_SetConfigurationBytes(ADC_PARAMS params) { // **Construct Configuration Byte** uint8_t config_byte = ADC_ConstructConfigBytes(params); //uint8_t adc_address= ADC_TARGET_BASE_ADDRESS + slot_id; printf("Writing Config: 0x%02X to ADC (0x%X)...\n", config_byte, ADC_TARGET_BASE_ADDRESS); // Wait for I2C Bus to be Free** while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS); // **Start I2C Write Transaction** // Prepare TX Buffer: gTxPacket[0]= config_byte; gTxADClen= 1; gTxADCcount= 0; gTxComplete= true; DL_I2C_startControllerTransfer(I2C_controller_INST, ADC_TARGET_BASE_ADDRESS, DL_I2C_CONTROLLER_DIRECTION_TX, gTxADClen); DL_I2C_enableInterrupt(I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER); while(!gTxComplete); // **Ensure STOP Condition is Sent** while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS); printf("Configuration Sent Successfully for 0x%X!\n", config_byte); } /* 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 */ int16_t ADC_ReadData(uint8_t slot_id, ADC_PARAMS params) { uint8_t adc_data[3] = {0}; // Buffer for ADC data (MSB, LSB, Config Byte) int16_t raw_adc = 0; /*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. */ uint8_t bytes_to_read= 3; uint8_t adc_address= ADC_TARGET_BASE_ADDRESS + slot_id; printf("Reading ADC Data from MCP3428...\n"); delay_cycles(100000); while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS); // Determine bytes to read //printf("Resolution for reading ADC is %d\n", resolution); // Request 3 Bytes from ADC** //DL_I2C_startControllerTransfer(I2C_controller_INST, DEF_TARGET_ADDR_ADC, DL_I2C_CONTROLLER_DIRECTION_RX, bytes_to_read); gRxADClen= bytes_to_read; gRxADCcount= 0; gRxComplete= false; DL_I2C_startControllerTransfer(I2C_controller_INST, adc_address, DL_I2C_CONTROLLER_DIRECTION_RX, gRxADClen); DL_I2C_enableInterrupt(I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_RXFIFO_TRIGGER); delay_cycles(100000); while(!gRxComplete); while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS); uint8_t config_adc_byte= gRxPacket[2]; uint8_t msb= gRxPacket[0]; uint8_t lsb= gRxPacket[1]; 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("Gain %d: \n", gain_multiplier); printf("Raw ADC Value: 0x%0X (%d)\n", raw_adc, raw_adc); return raw_adc; } /* Function to Convert ADC Reading to Voltage */ uint16_t ADC_ConvertToVoltage(int16_t adc_value, ADC_PARAMS params) { uint32_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 * ADC_VREF_MV) * 3)/max_adc_value; /*Formula for ADC to Voltage Conversion*/ //voltage = (adc_value / max_adc_value)* VREF/gain; //printf("Converted Voltage: %d mV\n", measured_voltage); 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 current_mA= 0; //uint8_t r_shunt= 0.1; //Convert ADC value to voltage across shunt resistor: uint16_t voltage_mV= ADC_ConvertToVoltage(adc_value, params); //Convert voltage drop across shunt resistor to current current_mA= (voltage_mV)/ 0.1; printf("Converted Current: %d mA.\n", current_mA); return current_mA; } void Battery_UpdateCurrentVoltage(ADC_PARAMS params){ for(uint8_t slot=0; slot< NUM_SLOTS; slot++ ){ //Set Configuration //ADC_SetConfigurationBytes(slot, params); //CH1: Voltage Setup int16_t raw_adc_voltage= ADC_ReadData(slot, params); batteries[slot].voltage= ADC_ConvertToVoltage(raw_adc_voltage, params); printf("Battery Voltage for slot %d is %u mV.\n", slot, batteries[slot].voltage); //CH2: Charge Current int16_t raw_adc_current= ADC_ReadData(slot, params); batteries[slot].current= raw_adc_current; printf("Battery current for slot %d is %u mA.\n", slot, batteries[slot].current); } }