| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- #include "battery.h"
- #include "ti/driverlib/dl_i2c.h"
- #include "ti/driverlib/m0p/dl_core.h"
- #include "ti_msp_dl_config.h"
- #include <adc.h>
- #include <stdint.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;
- static ADC_PARAMS adc_params;
- static ADC_MeasurementState adc_state = ADC_STATE_CONFIGURE;
- uint8_t ADC_ConstructConfigBytes(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*/
- void ADC_SetConfigurationBytes(ADC_PARAMS params) {
- // **Construct Configuration Byte**
- uint8_t config_byte = ADC_ConstructConfigBytes(params);
- // 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 = false;
- DL_I2C_flushControllerTXFIFO(I2C_controller_INST);
- DL_I2C_fillControllerTXFIFO(I2C_controller_INST, (uint8_t *)&gTxPacket, 1);
- 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);
- }
- /*
- 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
- */
- 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;
- printf("Reading ADC Data from MCP3428 for channel: %u\n", params.channel);
- gRxADClen = 3;
- 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);
- while (DL_I2C_getControllerStatus(I2C_controller_INST) &
- DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
- ;
- while (!gRxComplete)
- ;
- uint8_t config_adc_byte = gRxPacket[2];
- // Ready bit is bit 7
- bool ready = (config_adc_byte & 0x80) == 0;
- printf("Slot: %d | Config Byte: 0x%02X | READY Bit: %d\n", slot_id,
- 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.
- * 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.
- */
- 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};
- int16_t raw_adc = 0;
- uint8_t bytes_to_read = 3;
- uint8_t adc_address = ADC_TARGET_BASE_ADDRESS + slot_id;
- // printf("Reading ADC Data from MCP3428 for channel: %u\n", params.channel);
- if (DL_I2C_getControllerStatus(I2C_controller_INST) &
- DL_I2C_CONTROLLER_STATUS_BUSY_BUS) {
- printf("Error: I2C bus stuck! Resetting..\n");
- // DL_I2C_resetControllerTransfer(I2C_controller_INST);
- }
- 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);
- while (DL_I2C_getControllerStatus(I2C_controller_INST) &
- DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
- ;
- while (!gRxComplete)
- ;
- uint8_t msb = gRxPacket[0];
- uint8_t lsb = gRxPacket[1];
- uint8_t config_adc_byte = gRxPacket[2];
- 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;
- }
- // **Interrupt handler for ADC Read Completion**
- 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 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;
- return (uint16_t)measured_voltage;
- }
- /* Function to Convert ADC Reading to Voltage */
- 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:
- uint16_t voltage_mV = ADC_ConvertToVoltage(adc_value, params);
- uint8_t gain_multiplier = (1 << (params.gain - 1));
- // Convert voltage drop across shunt resistor to current
- current_mA = (voltage_mV) / (0.1 * gain_multiplier);
- // printf("Converted Current: %d mA.\n", current_mA);
- return (int16_t)current_mA;
- }
- void Battery_UpdateADCReading(uint8_t slot, uint8_t channel) {
- while (adc_state != ADC_STATE_DONE) {
- switch (adc_state) {
- case ADC_STATE_CONFIGURE:
- adc_params.channel = channel;
- // printf("Channel: %d\n", adc_params.channel);
- adc_params.resolution = 12;
- adc_params.continuous = 1;
- adc_params.gain = 1;
- ADC_SetConfigurationBytes(adc_params);
- adc_state = ADC_STATE_WAIT;
- break;
- case ADC_STATE_WAIT:
- if (ADC_CheckReadyBit(slot, adc_params)) {
- adc_state = ADC_STATE_READ;
- }
- break;
- case ADC_STATE_READ:
- if (channel == 0) {
- int16_t raw_adc_voltage = ADC_ReadData(slot, adc_params);
- batteries[slot].voltage =
- ADC_ConvertToVoltage(raw_adc_voltage, adc_params);
- printf("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);
- batteries[slot].current =
- ADC_ConvertToCurrent(raw_adc_current, adc_params);
- printf("Battery current for slot %d is %u mA.\n", slot,
- batteries[slot].current);
- adc_state = ADC_STATE_DONE;
- }
- break;
- default:
- channel = 0;
- adc_state = ADC_STATE_CONFIGURE;
- break;
- }
- }
- 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 >= min_voltage && voltage_mv < max_voltage) {
- 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;
- }
- }
|