Tool/software:
Hi Team
In my application, i am using I2C expander TCA9543A interfaced with MSP430FR-2475 MCU over the I2C channel and temperature sensor is also interfaced over I2C on the other side of the TCA9543A. The MSP430-FR-2475 is connected with I2C expander and this expander further connected with temperature sensor over the I2C. The temperature gives data to I2C expander and I2C expander gives this temperature data to MCU. I am looking for the sample code for this and want to understand how to use this TCA9543A control register to to read the temperature.
Can you please guide on this?
Below diagram shows my connections,
Regards,
Nitish
Why are you using the TCA9543A? Is there anything connected to the other channel?
Hi Clemens
We are using the same slave devices TMP102 sensor and in hardware we have I2C channel limitations to accommodate other I2C peripherals.
Thanks,
Nitish
we have multi same slave address devices which needs to be connected on same I2C port of MSP430. SInce only 2 ports are there we added this expander IC to connect temperature sensor and EEPROM IC to each I2C port like shown below.
The TCA9543A creates a direct connection between SDA/SCL and the enabled channel(s).
To access one of the TMP102 devices, first write the correct value to the TCA9543A's control register (1 or 2), then access the TMP102 directly.
Hi Clemens,
It would be helpful if you can guide on how to configure this control register for write, read and interrupt detection.
Thanks,
Nitish
For how to configure the control register for write or read accesses to the TMP102s, see my previous answer.
To detect interrupts, connect the TC9543A's /INT output to a GPIO of the MCU. When the MCU gets an interrupt signal, read the configuration register to detect which slave device has an active interrupt.
Hi
Without using interrupt also i can read/write the TMP102, right? Or interrupt is necessary to read the actual data?
The TCA9543A just passes through (and combines) the interrupt signals; it does not actually care about their meaning.
Hi
Is there any specific address of a control register as i could not find in datasheet?
Below is how my I2C driver code looks like. Can you please suggest how can i pass my control register value in below function arguments assuming expander address 0xE0?
/**
* @brief
* Initilization of I2C0
*
* @details
* Initilization of I2C0
*
* @param[in] None
*
* @return None
*/
void initI2C(void)
{
UCB0CTLW0 = UCSWRST; // Enable SW reset
UCB0CTLW0 |= UCMODE_3 | UCMST | UCSSEL__SMCLK | UCSYNC; // I2C master mode, SMCLK
#if I2C_SPEED_400KHZ
UCB0BRW = 40; // fSCL = SMCLK/40 = ~400kHz
#else
UCB0BRW = 160; // fSCL = SMCLK/160 = ~100kHz
#endif
UCB0I2CSA = SLAVE_ADDR_TEMP_I2C0; // Slave Address
UCB0CTLW0 &= ~UCSWRST; // Clear SW reset, resume operation
UCB0IE |= UCNACKIE;
}
/* For slave device with dev_addr, read the data specified in slaves reg_addr for I2C0.
* The received data is available in ReceiveBuffer
*
* dev_addr: The slave device address.
* Example: SLAVE_ADDR
* reg_addr: The register or command to send to the slave.
* Example: CMD_TYPE_0_SLAVE
* count: The length of data to read
* Example: TYPE_0_LENGTH
* @return: I2C mode
*/
I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count)
{
delay(10);
/* Initialize state machine */
MasterMode = TX_REG_ADDRESS_MODE;
TransmitRegAddr = reg_addr;
RXByteCtr = count;
TXByteCtr = 0;
ReceiveIndex = 0;
TransmitIndex = 0;
/* Initialize slave address and interrupts */
UCB0I2CSA = dev_addr;
UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts
UCB0IE &= ~UCRXIE; // Disable RX interrupt
UCB0IE |= UCTXIE; // Enable TX interrupt
UCB0CTLW0 |= UCTR + UCTXSTT; // I2C TX, start condition
if ( (dev_addr == SLAVE_ADDR_PD) || (dev_addr == SLAVE_ADDR_PD_PORT2) ) {
__bis_SR_register(GIE);
#if I2C_SPEED_400KHZ
delay(5);
#else
delay(50);
#endif
} else {
__bis_SR_register(LPM0_bits + GIE);
}
return MasterMode;
}
/* For slave device with dev_addr, writes the data specified in *reg_data for I2C0.
*
* dev_addr: The slave device address.
* Example: SLAVE_ADDR
* reg_addr: The register or command to send to the slave.
* Example: CMD_TYPE_0_MASTER
* *reg_data: The buffer to write
* Example: MasterType0
* count: The length of *reg_data
* Example: TYPE_0_LENGTH
* @return: I2C mode
*/
I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count)
{
delay(10);
/* Initialize state machine */
MasterMode = TX_REG_ADDRESS_MODE;
TransmitRegAddr = reg_addr;
//Copy register data to TransmitBuffer
#if ENABLE_CopyArray_API
CopyArray(reg_data, TransmitBuffer, count);
#else
memcpy(TransmitBuffer, reg_data, count);
#endif // ENABLE_CopyArray_API
TXByteCtr = count;
RXByteCtr = 0;
ReceiveIndex = 0;
TransmitIndex = 0;
/* Initialize slave address and interrupts */
UCB0I2CSA = dev_addr;
UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts
UCB0IE &= ~UCRXIE; // Disable RX interrupt
UCB0IE |= UCTXIE; // Enable TX interrupt
UCB0CTLW0 |= UCTR + UCTXSTT; // I2C TX, start condition
if ( (dev_addr == SLAVE_ADDR_PD) || (dev_addr == SLAVE_ADDR_PD_PORT2) ) {
__bis_SR_register(GIE);
#if I2C_SPEED_400KHZ
delay(5);
#else
delay(50);
#endif
} else {
__bis_SR_register(LPM0_bits + GIE);
}
return MasterMode;
}
//******************************************************************************
// I2C Interrupt ***************************************************************
//******************************************************************************
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
//Must read from UCB0RXBUF
uint8_t rx_val = 0;
switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
{
case USCI_NONE: break; // Vector 0: No interrupts
case USCI_I2C_UCALIFG: break; // Vector 2: ALIFG
case USCI_I2C_UCNACKIFG: // Vector 4: NACKIFG
UCB0IE &= ~UCRXIE;
MasterMode = NACK_MODE;
first_byte_received = FALSE;
__bic_SR_register_on_exit(CPUOFF);
break;
case USCI_I2C_UCSTTIFG: break; // Vector 6: STTIFG
case USCI_I2C_UCSTPIFG: break; // Vector 8: STPIFG
case USCI_I2C_UCRXIFG3: break; // Vector 10: RXIFG3
case USCI_I2C_UCTXIFG3: break; // Vector 12: TXIFG3
case USCI_I2C_UCRXIFG2: break; // Vector 14: RXIFG2
case USCI_I2C_UCTXIFG2: break; // Vector 16: TXIFG2
case USCI_I2C_UCRXIFG1: break; // Vector 18: RXIFG1
case USCI_I2C_UCTXIFG1: break; // Vector 20: TXIFG1
case USCI_I2C_UCRXIFG0: // Vector 22: RXIFG0
rx_val = UCB0RXBUF;
#if 1
if ( PD_Communication == TRUE ) {
PD_Communication = FALSE;
RXByteCtr = rx_val; /* In read operation the first byte received will be the length */
}
#endif
if ( RXByteCtr ) {
ReceiveBuffer[ReceiveIndex++] = rx_val;
RXByteCtr--;
}
if ( RXByteCtr == 1 ) {
UCB0CTLW0 |= UCTXSTP;
} else if ( RXByteCtr == 0 ) {
UCB0IE &= ~UCRXIE;
MasterMode = IDLE_MODE;
first_byte_received = FALSE;
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
break;
case USCI_I2C_UCTXIFG0: // Vector 24: TXIFG0
switch ( MasterMode ) {
case TX_REG_ADDRESS_MODE:
UCB0TXBUF = TransmitRegAddr;
if (RXByteCtr)
MasterMode = SWITCH_TO_RX_MODE; // Need to start receiving now
else
MasterMode = TX_DATA_MODE; // Continue to transmision with the data in Transmit Buffer
break;
case SWITCH_TO_RX_MODE:
UCB0IE |= UCRXIE; // Enable RX interrupt
UCB0IE &= ~UCTXIE; // Disable TX interrupt
UCB0CTLW0 &= ~UCTR; // Switch to receiver
MasterMode = RX_DATA_MODE; // State state is to receive data
UCB0CTLW0 |= UCTXSTT; // Send repeated start
if ( RXByteCtr == 1 ) {
//Must send stop since this is the N-1 byte
while((UCB0CTLW0 & UCTXSTT));
UCB0CTLW0 |= UCTXSTP; // Send stop condition
first_byte_received = FALSE;
}
break;
case TX_DATA_MODE:
if (TXByteCtr) {
UCB0TXBUF = TransmitBuffer[TransmitIndex++];
TXByteCtr--;
} else {
//Done with transmission
UCB0CTLW0 |= UCTXSTP; // Send stop condition
MasterMode = IDLE_MODE;
UCB0IE &= ~UCTXIE; // disable TX interrupt
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
break;
default:
__no_operation();
break;
}
break;
default: break;
}
}
Most I²C devices have register addresses. To write to a register, you write a byte with the register number, and then a byte with the value; to read a register, you write a byte with the register number, then read the byte with the value.
The TCA9543A has only a single register without an address. To write to the register, you just write a byte with the value; to read the register, you just read the byte with the value.
You have to change your state machine so that it works without starting in TX_REG_ADDRESS_MODE.
Hi
Can you please provide these functions that works without using TX_REG_ADDRESS_MODE and this TCA9543A can be used easily. Because all the other I2C peripherals are having its register addresses and if change this state machine, this can affects other functionalities.
Hi,
Can you suggest if below modified I2C driver would work with this expander?
void I2C_Write(uint8_t slaveAddress, uint8_t *data, uint8_t length)
{
txBuffer = data;
txLength = length;
txIndex = 0;
while (UCB1CTLW0 & UCTXSTP); // Ensure STOP condition is cleared
UCB1I2CSA = slaveAddress; // Set slave address
UCB1CTLW0 |= UCTR | UCTXSTT; // Transmit mode, Start condition
}
// I2C TX ISR for EUSCI_B1
#pragma vector = EUSCI_B1_VECTOR
__interrupt void EUSCI_B1_ISR(void)
{
switch (__even_in_range(UCB1IV, 0x1E))
{
case 0x16: // UCTXIFG0 (Transmit Buffer Empty)
if (txIndex < txLength) {
UCB1TXBUF = txBuffer[txIndex++]; // Send next byte
} else {
UCB1CTLW0 |= UCTXSTP; // Send STOP condition
UCB1IFG &= ~UCTXIFG0; // Clear flag
}
break;
case 0x12: // UCSTPIFG (Stop Condition Received)
UCB1IFG &= ~UCSTPIFG; // Clear flag
__bic_SR_register_on_exit(LPM0_bits); // Exit low-power mode
break;
}
}
void I2C_Read(uint8_t slaveAddress, uint8_t *buffer, uint8_t length)
{
rxBuffer = buffer;
rxLength = length;
rxIndex = 0;
while (UCB1CTLW0 & UCTXSTP); // Ensure STOP condition is cleared
UCB1I2CSA = slaveAddress; // Set slave address
UCB1CTLW0 &= ~UCTR; // Receiver mode
UCB1CTLW0 |= UCTXSTT; // Send Start condition
}
// I2C RX ISR for EUSCI_B1
#pragma vector = EUSCI_B1_VECTOR
__interrupt void EUSCI_B1_ISR(void)
{
switch (__even_in_range(UCB1IV, 0x1E))
{
case 0x18: // UCRXIFG0 (Receive Buffer Full)
if (rxIndex < rxLength - 1) {
rxBuffer[rxIndex++] = UCB1RXBUF; // Store received byte
} else {
UCB1CTLW0 |= UCTXSTP; // Send STOP condition
rxBuffer[rxIndex++] = UCB1RXBUF; // Store last byte
UCB1IFG &= ~UCRXIFG0; // Clear flag
}
break;
case 0x12: // UCSTPIFG (Stop Condition Received)
UCB1IFG &= ~UCSTPIFG; // Clear flag
__bic_SR_register_on_exit(LPM0_bits); // Exit low-power mode
break;
}
}
Thanks,
Nitish
Hi Nitish,
I think Clemens might comment about your code soon but as far as myself I am not a expert in writing code for i2c and I cannot give advice on your code
However to your original question I did find some helpful resources for you to look at as your original question was example code for the TCA9543A
Maybe this thread can help with your orginal question but please let me and clemens know
Regards,
Kameron
Hi
Can you please suggest how to combine below two I2C ISR code into one? As I found one ISR without TX_REG_ADDRESS_MODE and second one with TX_REG_ADDRESS_MODE.
Below function with TX_REG_ADDRESS_MODE:
#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
{
//Must read from UCB1RXBUF
uint8_t rx_val = 0;
switch(__even_in_range(UCB1IV, USCI_I2C_UCBIT9IFG)) {
case USCI_NONE: break; // Vector 0: No interrupts
case USCI_I2C_UCALIFG: break; // Vector 2: ALIFG
case USCI_I2C_UCNACKIFG: // Vector 4: NACKIFG
UCB1IE &= ~UCRXIE;
MasterMode = NACK_MODE;
__bic_SR_register_on_exit(CPUOFF);
break;
case USCI_I2C_UCSTTIFG: break; // Vector 6: STTIFG
case USCI_I2C_UCSTPIFG: break; // Vector 8: STPIFG
case USCI_I2C_UCRXIFG3: break; // Vector 10: RXIFG3
case USCI_I2C_UCTXIFG3: break; // Vector 12: TXIFG3
case USCI_I2C_UCRXIFG2: break; // Vector 14: RXIFG2
case USCI_I2C_UCTXIFG2: break; // Vector 16: TXIFG2
case USCI_I2C_UCRXIFG1: break; // Vector 18: RXIFG1
case USCI_I2C_UCTXIFG1: break; // Vector 20: TXIFG1
case USCI_I2C_UCRXIFG0: // Vector 22: RXIFG0
rx_val = UCB1RXBUF;
#if 1
if ( PD_Communication == TRUE ) {
PD_Communication = FALSE;
RXByteCtr = rx_val; /* In read operation the first byte received will be the length */
}
#endif
if ( RXByteCtr ) {
ReceiveBuffer[ReceiveIndex++] = rx_val;
RXByteCtr--;
}
if ( RXByteCtr == 1 ) {
UCB1CTLW0 |= UCTXSTP;
} else if ( RXByteCtr == 0 ) {
UCB1IE &= ~UCRXIE;
MasterMode = IDLE_MODE;
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
break;
case USCI_I2C_UCTXIFG0: // Vector 24: TXIFG0
switch ( MasterMode ) {
case TX_REG_ADDRESS_MODE:
UCB1TXBUF = TransmitRegAddr;
if (RXByteCtr)
MasterMode = SWITCH_TO_RX_MODE; // Need to start receiving now
else
MasterMode = TX_DATA_MODE; // Continue to transmision with the data in Transmit Buffer
break;
case SWITCH_TO_RX_MODE:
UCB1IE |= UCRXIE; // Enable RX interrupt
UCB1IE &= ~UCTXIE; // Disable TX interrupt
UCB1CTLW0 &= ~UCTR; // Switch to receiver
MasterMode = RX_DATA_MODE; // State state is to receive data
UCB1CTLW0 |= UCTXSTT; // Send repeated start
if ( RXByteCtr == 1 ) {
//Must send stop since this is the N-1 byte
while((UCB1CTLW0 & UCTXSTT));
UCB1CTLW0 |= UCTXSTP; // Send stop condition
}
break;
case TX_DATA_MODE:
if ( TXByteCtr ) {
UCB1TXBUF = TransmitBuffer[TransmitIndex++];
TXByteCtr--;
} else {
//Done with transmission
UCB1CTLW0 |= UCTXSTP; // Send stop condition
MasterMode = IDLE_MODE;
UCB1IE &= ~UCTXIE; // disable TX interrupt
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
break;
default:
__no_operation();
break;
}
break;
default: break;
}
}
Below without TX_REG_ADDRESS_MODE:
#pragma vector = EUSCI_B1_VECTOR
__interrupt void EUSCI_B1_ISR(void)
{
switch (__even_in_range(UCB1IV, 0x1E))
{
case 0x16: // UCTXIFG0 (Transmit Buffer Empty)
if (txIndex < txLength) {
UCB1TXBUF = txBuffer[txIndex++]; // Send next byte
} else {
UCB1CTLW0 |= UCTXSTP; // Send STOP condition
UCB1IFG &= ~UCTXIFG0; // Clear flag
}
break;
case 0x12: // UCSTPIFG (Stop Condition Received)
UCB1IFG &= ~UCSTPIFG; // Clear flag
__bic_SR_register_on_exit(LPM0_bits); // Exit low-power mode
break;
}
}
Thanks,
Nitish
Hi Nitish,
I am not a expert on i2c drivers and code for them,
I also agree with Clemens that changing your state machine so that it works without starting in TX_REG_ADDRESS_MODE is our best solution.
However I can primely help with the TCA9543A and if you have any other questions regarding that device specifically please let me know.
Regards,
Kameron
Hi
Can Clemens please get a chance to look into this code state machine as we are stuck here? Support for this is highly appreciative.
Thanks,
Nitish
Hi Nitish,
I think Clemens might take a look into this but alternatively I can move this thread internally to the TI team who has more knowledge about the MSP430 and its internal state machine and they can potentially help review your software you wrote.
Regards,
Kameron
Leave the interrupt handler and the other function as they were. The following functions should work (but are untested):
/* * Writes one byte without writing a register number. */ I2C_Mode I2C_Master_WriteDirect(uint8_t dev_addr, uint8_t data) { delay(10); /* Initialize state machine */ MasterMode = TX_DATA_MODE; TransmitBuffer[0] = data; TXByteCtr = 1; RXByteCtr = 0; ReceiveIndex = 0; TransmitIndex = 0; /* Initialize slave address and interrupts */ UCB0I2CSA = dev_addr; UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts UCB0IE &= ~UCRXIE; // Disable RX interrupt UCB0IE |= UCTXIE; // Enable TX interrupt UCB0CTLW0 |= UCTR + UCTXSTT; // I2C TX, start condition __bis_SR_register(LPM0_bits + GIE); return MasterMode; } /* * Reads one byte without writing a register number. * The result is in ReceiveBuffer[0] if ReceiveIndex==1. */ I2C_Mode I2C_Master_ReadDirect(uint8_t dev_addr) { delay(10); /* Initialize state machine */ MasterMode = RX_DATA_MODE; RXByteCtr = 1; TXByteCtr = 0; ReceiveIndex = 0; TransmitIndex = 0; /* Initialize slave address and interrupts */ UCB0I2CSA = dev_addr; UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts UCB0IE |= UCRXIE; // Enable RX interrupt UCB0IE &= ~UCTXIE; // Disable TX interrupt UCB0CTLW0 &= ~UCTR; // I2C RX UCB0CTLW0 |= UCTXSTT; // start condition while((UCB0CTLW0 & UCTXSTT)); if (MasterMode != NACK_MODE) { //Must send stop since this is the last byte UCB0CTLW0 |= UCTXSTP; // Send stop condition first_byte_received = FALSE; __bis_SR_register(LPM0_bits + GIE); } return MasterMode; }
Hi Clemens
Thanks for the fuctions.
Just wanted to confirm if below are the correct sequence to read TMP102,
I2C_Master_WriteDirect (expander address, 0x1); // 0x1 = channel select value
I2C_Master_Read(expander address);
Or should i follow below sequence,
I2C_Master_WriteDirect (expander address, 0x1);
I2C_Master_ReadReg1 (TMP102 slave address, TEMP_READ_REG, 2 * sizeof(uint8_t));
Thnaks,
Nitish
You access the switch itself only to reconfigure the connections. After you've enabled the connections, you access the devices on that channel directly. Use the second sequence.
Hi Clemens
We are using the below drivers (you shared above untested) to work with the I2C expander. When we check the I2C repsonse while enabling the channel, we get the NACK (no acknowledgment) from the slave I2C expander. Can you please suggest whether there is any issue with the below drivers?
Just for the confirmation only, we checked the I2C expander which is further connected with TMP102 temp sensor with STM32 MCU EVK and it is giving proper temperature reading and correctly working.
The only stage we are stuck at is with these MSP430 drivers.
/*
* Writes one byte without writing a register number.
*/
I2C_Mode I2C_Master_WriteDirect(uint8_t dev_addr, uint8_t data)
{
delay(10);
/* Initialize state machine */
MasterMode = TX_DATA_MODE;
TransmitBuffer[0] = data;
TXByteCtr = 1;
RXByteCtr = 0;
ReceiveIndex = 0;
TransmitIndex = 0;
/* Initialize slave address and interrupts */
UCB0I2CSA = dev_addr;
UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts
UCB0IE &= ~UCRXIE; // Disable RX interrupt
UCB0IE |= UCTXIE; // Enable TX interrupt
UCB0CTLW0 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(LPM0_bits + GIE);
return MasterMode;
}
/*
* Reads one byte without writing a register number.
* The result is in ReceiveBuffer[0] if ReceiveIndex==1.
*/
I2C_Mode I2C_Master_ReadDirect(uint8_t dev_addr)
{
delay(10);
/* Initialize state machine */
MasterMode = RX_DATA_MODE;
RXByteCtr = 1;
TXByteCtr = 0;
ReceiveIndex = 0;
TransmitIndex = 0;
/* Initialize slave address and interrupts */
UCB0I2CSA = dev_addr;
UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts
UCB0IE |= UCRXIE; // Enable RX interrupt
UCB0IE &= ~UCTXIE; // Disable TX interrupt
UCB0CTLW0 &= ~UCTR; // I2C RX
UCB0CTLW0 |= UCTXSTT; // start condition
while((UCB0CTLW0 & UCTXSTT));
if (MasterMode != NACK_MODE)
{
//Must send stop since this is the last byte
UCB0CTLW0 |= UCTXSTP; // Send stop condition
first_byte_received = FALSE;
__bis_SR_register(LPM0_bits + GIE);
}
return MasterMode;
}
Thanks,
Nitish
I do not have such a MSP430 device to test with.
Check with an oscilloscope whether these read/write accesses are correct.
Hi Clemens,
The modified I2C drivers you suggested above (without starting in TX_REG_ADDRESS_MODE) are working and now can be considered as tested.
Thanks for the support !!!
Regards,
Nitish