/* * Copyright (c) 2024, Texas Instruments Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ti/driverlib/dl_gpio.h" #include "ti/driverlib/dl_i2c.h" #include "ti_msp_dl_config.h" #include "ti/comm_modules/i2c/controller/i2c_comm_controller.h" #include #include I2C_Instance gI2C; /*Multiplexer Address*/ #define MULTIPLEXER_I2C_ADDR (0x70) /*Multiplexer channel*/ #define ADC_I2C_CHANNEL (0x01) volatile I2C_CommandInfo gCommand; I2C_ResponseInfo gResponse; volatile bool gSendCommand = true; /*Buffers to store ADC Data*/ /* Data to send to target */ uint8_t gTxData[MAX_DATA_SIZE] = {0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, \ 0x00,0x11,0x22,0x33,0x44,0x55, \ 0x66,0x77,0x88,0x99,0x12,0x23, \ 0x34,0x45,0x56,0x67,0x78,0x89}; /* Multiplexer TCA9548A: Address:0x70 The TCA9548A is example of a single-register device, which is controlled via I2C commands. Since it has 1 bit to enable or disable a channel, there is only 1 register needed, and the controller merely writes the register data after the target address, skipping the register number. ADC connected to channel 0 Both SDA and SL lines of the multiplexer is connected to VIN through pull-up resistors The following is the general procedure for a controller to access a target device: 1. If a controller wants to send data to a target: • Controller-transmitter sends a START condition and addresses the target-receiver. • Controller-transmitter sends data to target-receiver. • Controller-transmitter terminates the transfer with a STOP condition. 2. If a controller wants to receive or read data from a target: • Controller-receiver sends a START condition and addresses the target-transmitter. • Controller-receiver sends the requested register to read to target-transmitter. • Controller-receiver receives data from the target-transmitter. • Controller-receiver terminates the transfer with a STOP condition. The TCA9548A is example of a single-register device, which is controlled via I2C commands. Since it has 1 bit to enable or disable a channel, there is only 1 register needed, and the controller merely writes the register data after the target address, skipping the register number. */ /*Function for Multiplexer*/ void Multiplexer_SelectChannel(uint8_t channel) { uint8_t data = channel; printf("Scanning I2C Bus...\n"); // Ensure bus is idle before starting communication while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS); // Attempt to send START and the device address DL_I2C_startControllerTransfer(I2C_controller_INST, MULTIPLEXER_I2C_ADDR, DL_I2C_CONTROLLER_DIRECTION_TX, 1); // Wait for a response from the multiplexer while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS); // Send the channel selection byte DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &data, 1); // **CHECK FOR ADDRESS ACKNOWLEDGMENT** if (!(DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR)) { printf("Multiplexer detected at 0x70.\n"); return; } else{ printf("ERROR: Multiplexer (0x70) detected.\n"); } // Wait for transaction completion while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS); // Verify if the multiplexer accepted the command printf("Checking the Multiplexer Configuration.\n"); uint8_t response; DL_I2C_startControllerTransfer(I2C_controller_INST, MULTIPLEXER_I2C_ADDR, DL_I2C_CONTROLLER_DIRECTION_RX, 1); while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS); response = DL_I2C_receiveControllerData(I2C_controller_INST); if(response != channel){ printf("ERROR: Multiplexer did not set the correct channel!\n"); } else { printf("Multiplexer Active Channel: 0x%X\n", channel); } } int main(void) { SYSCFG_DL_init(); printf("............System Configuration Enabled...............\n"); /* Multiplexer Section */ printf("Selecting Multiplexer Channel...\n"); Multiplexer_SelectChannel(ADC_I2C_CHANNEL); NVIC_EnableIRQ(I2C_controller_INST_INT_IRQN); I2C_init(&gI2C); /* Default CommandInfo Object */ I2C_CommandInfo CommandInfo_Obj; CommandInfo_Obj.targetAddr = DEF_TARGET_ADDR; CommandInfo_Obj.commandType = READ_COMMAND; CommandInfo_Obj.addr = 0x20207C00; CommandInfo_Obj.dataArray = &gTxData[0]; CommandInfo_Obj.dataSize = 8; CommandInfo_Obj.crcEnable = false; gCommand = CommandInfo_Obj; while(1) { if(gSendCommand == true) { //Add a delay to allow conversion time (~1ms) delay_cycles(50000); // Adjust if needed //Frames a command packet and sends it to Target through I2C Write Command I2C_sendCommand(&gI2C, (I2C_CommandInfo *) &gCommand); printf("Requesting ADC Data from I2C address: 0x%X\n", gCommand.targetAddr); gI2C.status = I2C_STATUS_RX_STARTED; gI2C.rxMsg.ptr = 0; gI2C.dataLen = gCommand.dataSize; // Issues I2C Read Command to get the response from Target I2C_getResponse(&gI2C,gCommand.targetAddr); gSendCommand = false; } } } /*Interrupt for MCU -> ADC*/ void I2C_controller_INST_IRQHandler(void) { printf("I2C Interrupt Triggered to ADC!\n"); switch (DL_I2C_getPendingInterrupt(I2C_controller_INST)) { case DL_I2C_IIDX_CONTROLLER_RX_DONE: gI2C.rxMsg.len = gI2C.rxMsg.ptr; I2C_decodeResponse(&gI2C,&gResponse); gI2C.status = I2C_STATUS_RX_COMPLETE; printf("I2C RX COMPLETE!\n"); break; case DL_I2C_IIDX_CONTROLLER_TX_DONE: DL_I2C_disableInterrupt( I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER); gI2C.status = I2C_STATUS_TX_COMPLETE; printf("I2C TX COMPLETE!\n"); 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 (gI2C.rxMsg.ptr < MAX_BUFFER_SIZE) { gI2C.rxMsg.buffer[gI2C.rxMsg.ptr++] = DL_I2C_receiveControllerData(I2C_controller_INST); if(gI2C.rxMsg.ptr>0){ printf("Received Byte[%d]: 0x%02X\n", gI2C.rxMsg.ptr, gI2C.rxMsg.buffer[gI2C.rxMsg.ptr - 1]); } } 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); } } break; case DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER: printf("TX FIFO with data!\n"); gI2C.status = I2C_STATUS_TX_INPROGRESS; /* Fill TX FIFO with bytes to send */ if (gI2C.txMsg.ptr < gI2C.txMsg.len) { gI2C.txMsg.ptr += DL_I2C_fillControllerTXFIFO( I2C_controller_INST, &gI2C.txMsg.buffer[gI2C.txMsg.ptr], gI2C.txMsg.len - gI2C.txMsg.ptr); } break; case DL_I2C_IIDX_CONTROLLER_ARBITRATION_LOST: printf("Interrupt index for I2C controller Arbitration Lost!\n"); case DL_I2C_IIDX_CONTROLLER_NACK: printf("Interrupt index for Address/Data NACK!\n"); gI2C.status = I2C_STATUS_ERROR; break; default: break; } }