| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- /*
- 20.02.2025:
- Flowchart of the code:
- 1. MCU requests ADC data over I2C
- 2. ADC is connected to the MULTIPLEXER.
- 3. Interrupt triggers when data is available.
- 4. Interrupt moves RECEIVED bytes into RXFIFO Buffer
- 5. Interrupt marks reception complete RX_DONE
- 6. Read ADC_Data and retrieve the final ADc value
- 7. Convert ADC value to Voltage
- 24.02.2025:
- Working Code for Reading Analog Signals from ADC to MCU through Multiplexer
- - configured the registers with Channel, Resolution, Mode(Continuous or Single-mode) and Gain: Construct_Config_Byte()
- - SET configuration registers to ADC: SetConfiguration()
- - Read the Analog Output: Read_ADC_Data()
- - Convert Analog output to voltage in Volts: Convert_ADC_To_Voltage()
- - Commented out interrupts for the time being.
- - Adding code for Pi to READ ADC voltages in mV.
- 27.02.2025:
- DAC function to write to Channel A in Fast Mode and return the Analog output
- */
- #include "i2c_target.h"
- #include "ti/devices/msp/peripherals/hw_dac12.h"
- #include "ti/driverlib/dl_adc12.h"
- #include "ti/driverlib/dl_gpio.h"
- #include "ti/driverlib/dl_i2c.h"
- #include "ti/driverlib/m0p/dl_core.h"
- #include "ti_msp_dl_config.h"
- #include "ti/comm_modules/i2c/controller/i2c_comm_controller.h"
- #include <stdint.h>
- #include <stdio.h>
- #include <string.h>
- #include "multiplexer.h"
- #include "adc.h"
- #include "dac.h"
- #include "battery.h"
- #include "i2c_target.h"
- #include "cc_cv_charging.h"
- I2C_Instance gI2C;
- I2C_ResponseInfo gResponse;
- BatteryData battery_data;
- /*****************/
- /*Register for the MCU*/
- #define REGISTER_SIZE 256
- uint8_t registers[REGISTER_SIZE]= {1, 2, 3, 4, 5};
- /*Counters for TX length and bytes sent*/
- uint32_t gTxLen, gTxCount;
- /* Counters for RX length and bytes sent*/
- uint32_t gRxLen, gRxCount;
- /* Boolean to know when a stop command was issued */
- bool gStopReceived = false;
- uint8_t gTxData[MAX_DATA_SIZE] = {0} ;
- /*
- Scans all the addresses of the peripherals:
- */
- void I2C_scanBus() {
- printf("Scanning I2C Bus...\n");
- // **Step 1: Reset I2C Controller if Busy**
- if (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS) {
- printf("I2C Bus Busy! Resetting I2C Controller...\n");
- DL_I2C_disableController(I2C_controller_INST); // Disable I2C
- delay_cycles(20000);
- DL_I2C_enableController(I2C_controller_INST); // Re-enable I2C
- delay_cycles(20000);
- }
- // **Step 2: Scan I2C Bus**
- for (uint8_t addr = 0x08; addr < 0x78; addr++) { // Valid I2C Address Range
- DL_I2C_startControllerTransfer(I2C_controller_INST, addr, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
- delay_cycles(5000);
-
- if (!(DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR)) {
- printf("Device found at: 0x%02X\n", addr);
- }
- }
- printf("I2C Scan Complete!\n");
- }
- /*Function to get the RDY bit from ADC*/
- bool Check_ADC_Ready()
- {
- uint8_t config_byte = 0;
- /*
- 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
- */
- DL_I2C_startControllerTransfer(I2C_controller_INST, ADC_TARGET_BASE_ADDRESS, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
- config_byte = DL_I2C_receiveControllerData(I2C_controller_INST);
- return (config_byte & 0x80) == 0; // Check if RDY bit is cleared in bit 7
- }
- /*
- Function to initialize MCU as target: Pi communicates as the Controller and MCU as the Target
- */
- /*Interrupt for MCU -> ADC
- * CASE: DL_I2C_IIDX_CONTROLLER_RX_DONE: ADC Reception Complete
- - ADC has finished sending data and it's fully received.
- - gI2C.rxMsg.len = gI2C.rxMsg.ptr:
- - Stores the received data length in the response buffer.
- - I2C_decodeResponse():
- - Decodes the received response.
- - gI2C.status = I2C_STATUS_RX_COMPLETE:
- - Marks reception is complete.
- * CASE: DL_I2C_IIDX_CONTROLLER_TX_DONE: Data Transmit to ADC complete
- - DL_I2C_disableInterrupt(..): Disables the TXFIFO interrupt since data is now sent
- * CASE: DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER: Receive Data in FIFO
- - The I2C Receive FIFO has data ready to be read.
- - while (DL_I2C_isControllerRXFIFOEmpty(...) != true): Loops until the RX FIFOis empty (READ all available bytes)
- - Inside the while loop:
- - If buffer has SPACE, store the received byte
- - Prints each received byte in HEXADECIMAL format for debugging
- - IF BUFFER is FULL, avoids OVERFLOW by discarding extra byte.
- * CASE: DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER: Transmit Data in FIFO
- - If there is still data to send:
- gI2C.txMsg.ptr += DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &gI2C.txMsg.buffer[gI2C.txMsg.ptr], gI2C.txMsg.len - gI2C.txMsg.ptr);
- */
- void I2C_controller_INST_IRQHandler(void)
- {
- //printf("I2C Interrupt Triggered to ADC!\n");
- switch (DL_I2C_getPendingInterrupt(I2C_controller_INST))
- { /*START Condition*/
- case DL_I2C_IIDX_CONTROLLER_START:
- //gTxADCcount= 0;
- gRxADCcount= 0;
- DL_I2C_flushControllerTXFIFO(I2C_controller_INST);
- break;
- case DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER:
- gI2C.status= I2C_STATUS_RX_INPROGRESS;
- /* Store bytes received from target in Rx Msg Buffer */
- while (DL_I2C_isControllerRXFIFOEmpty(I2C_controller_INST) != true) {
- if (gRxADCcount< gRxADClen) {
- gRxPacket[gRxADCcount] = DL_I2C_receiveControllerData(I2C_controller_INST);
- printf("Received Byte[%d]: 0x%02X\n", gRxADCcount, gRxPacket[gRxADCcount]); // Debug print
- gRxADCcount++;
-
- } else {
- //printf("ERROR: RX Buffer Overflow! ptr=%d MAX_BUFFER_SIZE=%d\n", gI2C.rxMsg.ptr, MAX_BUFFER_SIZE);
- /* Ignore and remove from FIFO if the buffer is full */
- DL_I2C_receiveControllerData(I2C_controller_INST);
- }
- }
- if (gRxADCcount == gRxADClen){
- //printf("ADC Bytes Received!\n");
- DL_I2C_enableInterrupt(I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_STOP);
- //gRxComplete = true;
- }
- break;
- /*TRANSMIT data to ADC*/
- case DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER:
- printf("TX FIFO with data!\n");
- gI2C.status= I2C_STATUS_TX_INPROGRESS;
- if(gTxADCcount<gTxADClen){
- gTxADCcount+= DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &gTxPacket[gTxADCcount], (gTxADClen-gTxADCcount));
- } else{
- /*Prevent overflow and just ignore data*/
- DL_I2C_fillTargetTXFIFO(I2C_controller_INST, (uint8_t[]){0x00}, 1);
- }
- break;
- /*STOP condition*/
- case DL_I2C_IIDX_CONTROLLER_STOP:
- gTxComplete= true;
- gRxComplete = true;
- printf("I2C Stop Detected- RX Complete");
- case DL_I2C_IIDX_CONTROLLER_ARBITRATION_LOST:
- //printf("Interrupt index for I2C controller Arbitration Lost!\n");
- break;
- case DL_I2C_IIDX_CONTROLLER_NACK:
- //printf("I2C NACK Received\n");
- if((gI2C.status== I2C_STATUS_RX_STARTED)|| (gI2C.status= I2C_STATUS_TX_STARTED)){
- gI2C.status= I2C_STATUS_ERROR;
- }
- break;
- default:
- break;
- }
- }
- /**** Interrupt for Pi to MCU ****/
- void I2C_target_INST_IRQHandler(void) {
- //printf("I2C Interrupt Triggered to MCU (TARGET)!\n");
- uint8_t receivedCommand= 0;
- uint32_t status = DL_I2C_getPendingInterrupt(I2C_target_INST);
- //ADC_PARAMS params;
- switch (status) {
- /* START condition detected */
- case DL_I2C_IIDX_TARGET_START:
- piTxCount= 0;
- piRxCount= 0;
- piTxComplete= false;
- DL_I2C_flushTargetTXFIFO(I2C_target_INST);
- break;
- /* STOP condition detected */
- case DL_I2C_IIDX_TARGET_STOP:
- piTxComplete= true;
- piRxComplete = true;
- break;
- /* TX FIFO trigger (Pi is reading data from MCU) */
- /*TXFIFO to GET battery status is triggered when command is 0x01
- - Pi on request of 0x01 will get a response of the battery status for all the slots
- - Battery_StateUpdate function is called, which in turn calls the Battery_ReadState funtion to set the state of the batteries
- -Pi on command of [0x02, slot_id] will GET the 'Battery Data' which is voltage, current and temperature for a given slot.
- - MCU reads the slot_id from Pi using DL_I2C_receiveTargetData()
- - piTxCount is set to 0
- - piTxLen is the sizeof BatteryData struct which is 7 bytes
- - If the requested slot is correct then:
- - battery pointer variable points to the memory of the requested slot
- - the values of voltage, current and temperature are then stored in battery_data struct
- - Once the values are in BatteryData struct we wait for the bus to be free
- - Next we send the BatteryData to Pi using DL_I2C_fillTargetTXFIFO()
- - Reset the TX counter for the next data.
- */
- case DL_I2C_IIDX_TARGET_TXFIFO_TRIGGER:
- printf("TX to PI triggered!\n");
- if(!DL_I2C_isTargetTXFIFOEmpty(I2C_target_INST)){
- receivedCommand= DL_I2C_receiveTargetData(I2C_target_INST);
- printf("Received Command: 0x%02X\n", receivedCommand);
- if (receivedCommand == CMD_GET_BATTERY_STATUS){
- printf("Battery status received.\n");
- Battery_StateUpdate();
- if(piTxCount < piTxLen){
- while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
- DL_I2C_fillTargetTXFIFO(I2C_target_INST, &piTxPacket[piTxCount], 1);
- piTxCount++;
-
- }
- else {
- /*
- * Fill FIFO with 0x00 if more data is requested than expected piTxLen
- */
- while (
- DL_I2C_transmitTargetDataCheck(I2C_target_INST, 0x00) != false);
- }
- }
- else if (receivedCommand == CMD_GET_BATTERY_DATA){
- uint8_t requestedSlot= DL_I2C_receiveTargetData(I2C_target_INST);
- while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
- printf("Battery Data!\n");
- piTxCount= 0;
- piTxLen= sizeof(BatteryData);
- BatteryData battery_data;
- if(requestedSlot < NUM_SLOTS){
- Battery *battery= &batteries[requestedSlot];
- battery_data.slot_id = battery-> slot_id;
- battery_data.voltage= battery-> voltage;
- battery_data.current= battery-> current;
- battery_data.temperature= battery-> temperature;
- while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
- piTxCount += DL_I2C_fillTargetTXFIFO(I2C_target_INST, (uint8_t*)&battery_data, piTxLen);
- while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
- if(piTxCount >= piTxLen){
- piTxComplete= true;
- piTxCount=0;
- }
- } else{
- printf("Invalid Slot ID: %d\n.", requestedSlot);
- }
-
- }
- }
-
- break;
- /* TARGET_Rx FIFO trigger (Pi is writing data to MCU) */
- /*Pi SET battery data limits for each slot, where:
- - RXFIFO buffer is filled if the command from Pi is 0x03
- - Creating a temporary buffer named ´rxbuffer´
- - sizeof(BatteryLimitMsg): 11 bytes (1 byte: slot_id, 2 bytes: min_voltage; max_voltage; cut_off_current; capacitance; charge_fraction)
- - rx_buffer stores the data from Pi.
- - if all the expected bytes are received from Pi then,
- - memcpy() to copy the block of address from the temporary buffer to the BatteryLimitMsg structure
- - Why?, A: It copies the specified number of bytes from one memory location to another regardless of the type of the data stored.
- - verify if the received slot_id is less than NUM_SLOTS, where slot_id count starts from 0 then:
- - create a pointer variable for 'Battery'
- - battery_limits.slot_id: index of the battery slot to be updated
- - &batteries[battery_limits.slot_id]: gets the memory address of the battery in that slot
- - Accessing the structure members of Battery using -> operator. This allows efficient access to the structure's members
- without directly using the structure variable.
- */
- case DL_I2C_IIDX_TARGET_RXFIFO_TRIGGER:
- printf("RX FIFO triggered from PI.\n");
- if(!DL_I2C_isTargetRXFIFOEmpty(I2C_target_INST)){
- receivedCommand= DL_I2C_receiveTargetData(I2C_target_INST);
- printf("Received Command: 0x%02X\n", receivedCommand);
- if(receivedCommand == CMD_SET_BATTERY_LIMIT){
- uint8_t rx_buffer[sizeof(BatteryLimitMsg)];
- uint8_t index= 0;
- while (!DL_I2C_isTargetRXFIFOEmpty(I2C_target_INST)){
- if(index < sizeof(BatteryLimitMsg)){
- rx_buffer[index]= DL_I2C_receiveTargetData(I2C_target_INST);
- printf("Received Byte[%d]: 0x%02X\n", index, rx_buffer[index]);
- index++;
-
- }
- else{
- DL_I2C_receiveTargetData(I2C_target_INST);
- }
- }
- printf("Total Bytes Received: %d (Expected: %d)\n", index, sizeof(BatteryLimitMsg));
- if(index == sizeof(BatteryLimitMsg)){
- printf("Received Battery Limits.\n");
- BatteryLimitMsg battery_limits;
- memcpy(&battery_limits, rx_buffer, sizeof(BatteryLimitMsg));
- if(battery_limits.slot_id < NUM_SLOTS){
- Battery *battery = &batteries[battery_limits.slot_id];
- battery -> min_voltage = battery_limits.min_voltage;
- battery -> max_voltage = battery_limits.max_voltage;
- battery -> cut_off_current = battery_limits.cut_off_current;
- battery -> capacitance = battery_limits.capacitance;
- battery -> charge_fraction = battery_limits.charge_fraction;
- printf("\n Received Battery Limits for slot %d: \n", battery_limits.slot_id);
- printf(" Min Voltage: %d mV (0x%04X)\n", battery_limits.min_voltage, battery_limits.min_voltage);
- printf(" Max Voltage: %d mV (0x%04X)\n", battery_limits.max_voltage, battery_limits.max_voltage);
- printf(" Cutoff Current: %d mA (0x%04X)\n", battery_limits.cut_off_current, battery_limits.cut_off_current);
- printf(" Capacitance: %d µF (0x%04X)\n", battery_limits.capacitance, battery_limits.capacitance);
- printf(" Charge Fraction: %d%% (0x%02X)\n", battery_limits.charge_fraction, battery_limits.charge_fraction);
- }
- }
- }
- }
-
- break;
-
- /* Arbitration lost or NACK */
- case DL_I2C_IIDX_TARGET_ARBITRATION_LOST:
- printf("Arbitration Lost.\n");
- break;
- default:
- printf("Unknown Interrupt.\n");
- break;
- }
- }
- void Reset_I2C_Bus() {
- printf("I2C Bus is stuck! Resetting...\n");
-
- // Disable I2C Controller
- DL_I2C_disableController(I2C_controller_INST);
- delay_cycles(50000);
- // Re-enable I2C Controller
- DL_I2C_enableController(I2C_controller_INST);
- delay_cycles(50000);
- // Check if bus is free now
- uint32_t status = DL_I2C_getControllerStatus(I2C_controller_INST);
- printf("I2C Bus Status After Reset: 0x%08X\n", status);
- }
- /********MAIN function*************/
- int main(void)
- {
- // Initialize System and I2C
- SYSCFG_DL_init();
- // Initialize battery array and default params
- Battery_Init();
-
- //Reset_I2C_Bus();
- NVIC_EnableIRQ(I2C_target_INST_INT_IRQN);
- //NVIC_EnableIRQ(I2C_controller_INST_INT_IRQN);
- printf("............System Configuration Enabled...............\n");
-
- //Multiplexer
- Multiplexer_SelectChannel(I2C_CHANNEL);
- //I2C_scanBus();
- I2C_init(&gI2C);
- // *Configure ADC*
- ADC_PARAMS adc_params={
- .channel= 1,
- .resolution= 12,
- .continuous= 1,
- .gain= 1
- };
- //printf("Configuring ADC....\n");
- //**Set Configuration Register for ADC**
- //ADC_SetConfigurationBytes(adc_params);
- //uint16_t channel_a_value= 3300; // in mVolts
-
-
- while (1)
- {
-
- //Battery_UpdateCurrentVoltage(adc_params);
- //for(uint8_t slot_id= 0; slot_id < NUM_SLOTS; slot_id++){
- // CC_CV_ControlCharging(slot_id);
- //}
- //delay_cycles(5000);
- //DAC_fastWrite(channel_a_value);
- delay_cycles(100000);
- }
- }
-
-
|