/* 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.h" #include "src/peripherals/adc/adc_interface.h" #include "ti_msp_dl_config.h" #include #include "src/battery_data/battery.h" #include "src/config.h" /* * 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) if (params->continuous == 1) { config |= (1 << 4); // Continous mode } else { // One-Shot mode // Bit set to zero, BUT the Ready bit needs to be set to start meausrement // (read/not write bit) config |= (1 << 7); } 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; case 8: config |= (0b11); 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) { controllerTxPackage.packet[0] = construct_config_byte(params); // Wait for I2C Bus to be Free** uint32_t n_cycles = 0; while ((DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS) && n_cycles++ < MAX_I2C_WAIT_RX) ; if (n_cycles == MAX_I2C_WAIT_RX) { printf("Error in reading from I2C Bus: Bus is not getting ready on config byte\n"); } if(controllerTxPackage.packet[0] == 0xFF){ // this clause can only happen if the internal memory management is messed up?! // the config function should take care that this is never the case #ifdef DEBUG_ADC printf("[ADC] Unable to send config bytes\n"); #endif *battery_slots[slot_id].state = SLOT_ERR_CONFIGBYTE; return false; } // Prepare TX Buffer controllerTxPackage.len = 1; controllerTxPackage.count = 0; controllerTxPackage.complete = false; i2c_hal.write(battery_slots[slot_id].adc_addr); n_cycles = 0; while(!controllerTxPackage.complete && n_cycles++ < MAX_I2C_WAIT_RX); if (n_cycles == MAX_I2C_WAIT_RX) { #ifdef DEBUG_ADC printf("[ADC] No Response to the config byte!\n"); #endif return false; } return true; } /* 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 = battery_slots[slot_id].adc_addr; controllerRxPackage.len = 3; controllerRxPackage.count = 0; controllerRxPackage.complete = false; i2c_hal.read(adc_address); // Ready bit is bit 7 while(!controllerRxPackage.complete); uint8_t config_adc_byte = controllerRxPackage.packet[2]; bool ready = (config_adc_byte & 0x80) == 1; return ready; } static int16_t read_adc_raw_data(uint8_t slot_id, ADC_Params *params) { // Buffer for ADC data (MSB, LSB, Config Byte) uint32_t raw_adc = 0; controllerRxPackage.len = 3; controllerRxPackage.count = 0; controllerRxPackage.complete = false; i2c_hal.read(battery_slots[slot_id].adc_addr); uint32_t n_cycles = 0; while(!controllerRxPackage.complete && n_cycles++ < MAX_I2C_WAIT_RX); if (n_cycles == MAX_I2C_WAIT_RX) { return 0xffff; } uint8_t msb = controllerRxPackage.packet[0]; uint8_t lsb = controllerRxPackage.packet[1]; uint8_t config_adc_byte = controllerRxPackage.packet[2]; uint32_t max_adc_val = 0; switch (params->resolution) { case 12: // 12-bit raw_adc = ((msb & 0b00001111) << 8) | lsb; max_adc_val = 4096; break; case 14: // 14-bit raw_adc = ((msb & 0b00111111) << 8) | lsb; max_adc_val = 16384; break; case 16: // 16-bit raw_adc = ((msb & 0b11111111) << 8) | lsb; max_adc_val = 65536; break; default: //printf("Error: Unknown ADC Resolution!\n"); break; } return raw_adc * params->factor * 2048 / (max_adc_val/2) / params->gain; } ADC_Interface adc_hal= { .configure= adc_configure, .read_voltage = read_adc_raw_data, .is_ready= adc_is_ready, };