This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
I'm trying to use the I2C protocol to communicate between the MSP-EXP430G2 evaluation board (having the MSP430G2533 micro-controller) and a photo-detector (slave).
I've connected 2.2 K Ohms pull external resistors from the SDA and SCL lines pulled-up to the VDD (3.4 volts).
The slave address is 0x2A.
The protocol to communicate with the slave requires the following sequence(shown in the figure below) that I've stored the TX_DATA[8] array.
The code I'm using is pasted here. The problem is that: I'm either getting no ACK signal from the slave (this happens 90% of the times with some random value of the pull up), or sometimes when I get the ACK signal apart from the address there is no other data being transmitted (this happens only 10% of the time).
When I single step in the code in CCS Studio, the code gets stuck in the I2C transmit interrupt : __bis_SR_register(CPUOFF + GIE); and does not come out.
Does anyone have any idea why it would be that way? or did anyone face any similar problem before? any help would be highly appreciated.
// Communicating with the I2C Slave for data transfer from the photo-detector
// TI Development starter kit board is used here
#include <msp430.h>
/*
* main.c
*/
void I2C_Initialise(void);
void I2C_Transmit(void);
void I2C_Receive(void);
int TransmitByteCtr = 8;
unsigned char PointerRxData; // Pointer to RX data
int Rx = 0;
unsigned char TX_DATA[8] = {0x2A, 0x00, 0x89, 0x2A, 0x00, 0x09, 0x2A, 0x03};
int main( void )
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
//DISABLE_INT; // Disable all the interrupts
//ClockInitialise();
// Both are Port 1: Port 1.6 Port 1.7 Bits are set for I2C using the port registers, SDA = P1.7 SCL = P1.6 //
P1DIR |= 0xC0;
P1SEL |= BIT6 + BIT7; // Assign I2C pins to USCI_B0 //
P1SEL2|= BIT6 + BIT7; // Assign I2C pins to USCI_B0
I2C_Initialise();
while(1)
{
//Transmit process
Rx = 0;
I2C_Transmit();
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
//Receive process
Rx = 1;
I2C_Receive();
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
}
}
//-------------------------------------------------------------------------------
// The USCI_B0 data ISR is used to move received data from the I2C slave
// to the MSP430 memory. It is structured such that it can be used to receive
//-------------------------------------------------------------------------------
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
if(Rx == 1)
{ // Master Recieve?
PointerRxData = UCB0RXBUF; // Get RX data
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
else
{ // Master Transmit
if (TransmitByteCtr<8) // Check TX byte counter
{
UCB0TXBUF = TX_DATA[TransmitByteCtr]; // Load TX buffer
TransmitByteCtr++; // Decrement TX byte counter
}
else
{
UCB0CTL1 |= UCTXSTP; // I2C stop condition
IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
}
}
void I2C_Initialise(void)
{
IE2 |= UCB0RXIE; //Enable RX interrupt
IE2 |= UCB0TXIE; // Enable TX interrupt
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz
UCB0BR1 = 0;
UCB0I2CSA = 0x2A; // Slave Address is 02Ah
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
}
void I2C_Transmit(void)
{
// while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
}
void I2C_Receive(void)
{
UCB0CTL1 &= ~UCTR ; // Clear UCTR
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCTXSTT; // I2C start condition
while (UCB0CTL1 & UCTXSTT); // Start condition sent?
UCB0CTL1 |= UCTXSTP; // I2C stop condition
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
}
I
Hi,
are you using a logic analyzer to view the communication between the devices? Could you provide screenshots of the failures and "successes" you are seeing? Also, what value does the slave device recommend for the pull-up resistor?
The portion of the code __bis_SR_register(CPUOFF + GIE); puts the device into a low power mode (LPM) and then the rest of the processing is done in the interrupt service routine(ISR). If you set breakpoints within the ISR you should see that code is executing there.
I'll take a closer look at your code and let you know if I see anything out of the ordinary. I also recommend taking a look at the application report: Solutions to Common eUSCI and USCI Serial Communication Issues on MSP430 MCUs
Best regards,
Caleb Overbay
Hi Caleb,
Yes I did make progress and am able to transmit data from the master successfully.
I need some help with the Rx part of the code now.
After this sequence, the master requests the slave for the data on the SDA line, however the master must generate the clock for the data, do you've any clues of how to go about generating a read ISR for 8 bytes of data totally`?
After I read those bits of data the next step is to store them in the memory. Any hints for that?
Hi,
Reading data with I2C is certainly more tricky than writing. It depends on the read protocol specified in the slave devices data sheet, so be sure to familiarize yourself with it. Typically though the sequence is this:
Also, be mindful of when to send the stop sequence. On the MSP430G2553 the stop sequence must be sent on N-1th byte received. Sections 5.2 and 5.3 of the Solutions to Common eUSCI and USCI Serial Communication Issues on MSP430 MCUs go into this in more detail as well as the devices User's Guide.
Finally, I've attached some example code that performs I2C reads and writes from the MSP430G2553 as the master. This code follows the sequence specified in the application report I've linked to previously. Please use this as a guide for how to read multiple or single bytes from an I2C slave.
#include <msp430.h> #include <stdint.h> /* Slave Address */ #define Slave_Addr 0x28 /* I2C Communication States */ typedef enum { Idle, NACK, TX_Reg_Index, RX_Data, TX_Data, Switch_to_RX, Timeout }I2C_State; /* I2C Write and Read Functions */ I2C_State I2C_Write_Reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t cnt); I2C_State I2C_Read_Reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t cnt); /* Global Variables */ uint8_t I2C_Reg_Index = 0; uint8_t *I2C_Reg_Data; uint8_t TXByteCtr = 0; uint8_t RXByteCtr = 0; I2C_State Comm_State = Idle; uint8_t Write_buffer[10]; uint8_t Read_buffer[10]; int main(void){ WDTCTL = WDTPW | WDTHOLD; // Stop watch dog timer /* Initialize clock system*/ BCSCTL1=CALBC1_8MHZ; DCOCTL=CALDCO_8MHZ; BCSCTL2|=DIVS_2; //SMCLK divider = 4; /* Initialize I2C pins */ P1SEL|=BIT6+BIT7; P1SEL2|=BIT6+BIT7; // P1.6=SCL (I2C) P1.7=SDA (I2C) /* Initialize USCI_B for I2C Communication */ UCB0CTL1 |= UCSWRST; // Enable SW reset UCB0CTL1 |=UCSSEL_3 + UCTR; // select SMCLK, transmitter mode UCB0CTL0 |=UCMODE_3 + UCMST + UCSYNC; // set I2C MODE MASTER MODE UCB0BR0=20; // clk divider from SMCLK, 100kHz UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation UCB0I2CIE |= UCNACKIE; // Enable NACK interrupt /* Initialize TimerA for timeout detection */ TACCR0 = 10000; // 5ms TACCTL0 |= CCIE; // Enable CCR0 interrupts TACTL |= TASSEL_2; // SMCLK = 2MHz __delay_cycles(7000000); // Wait for slave to setup __enable_interrupt(); // Enable global interrupts /* Example of writing to slave */ Write_buffer[0] = 0x80; Write_buffer[1] = 0x0C; I2C_Write_Reg(Slave_Addr, 0x3F, &Write_buffer[0], 1); // Write 1 byte to slave register 0x3F if(Comm_State != Idle) { // Error during transmission } I2C_Write_Reg(Slave_Addr, 0x3D, &Write_buffer[1], 1); // Write 1 byte to slave register 0x3D if(Comm_State != Idle) { // Error during transmission } /* Example of reading from slave */ I2C_Read_Reg(Slave_Addr, 0x20, Read_buffer, 1); // Read 1 byte from register 0x20 if(Comm_State != Idle) { // Error during transmission } I2C_Read_Reg(Slave_Addr, 0x20, Read_buffer, 8); // Read 8 bytes from register 0x20 if(Comm_State != Idle) { // Error during transmission } __bis_SR_register(CPUOFF); // Enter LPM0 w/ interrupts } /* Input: dev_addr - slave I2C address reg_addr - address of the first register to be read *reg_data - pointer to the location where received data will be stored *cnt - number of bytes to be read */ I2C_State I2C_Read_Reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t cnt) { /* Initialize state machine */ Comm_State = TX_Reg_Index; I2C_Reg_Index = reg_addr; I2C_Reg_Data = reg_data; RXByteCtr = cnt; TXByteCtr = 0; /* Initialize slave address and interrupts */ UCB0I2CSA = dev_addr; IFG2 &= ~(UCB0TXIFG + UCB0RXIFG); // Clear any pending interrupts IE2 &= ~UCB0RXIE; // Disable RX interrupt IE2 |= UCB0TXIE; // Enable TX interrupt /* Start I2C communication */ TACTL |= MC_1 + TACLR; // Start timeout count (5ms) while (UCB0CTL1 & UCTXSTP) // Ensure stop condition got sent { if(Comm_State == Timeout) return Comm_State; } TACTL &= ~MC_1; // Stop timer TACTL |= MC_1 + TACLR; // Clear and restart timeout UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition __bis_SR_register(CPUOFF); // Enter LPM0 w/ interrupts TACTL &= ~MC_1; // Stop timer return Comm_State; } /* Input: dev_addr - slave I2C address reg_addr - address of the register to write *reg_data - pointer to the location with data to write *cnt - number of bytes to write */ I2C_State I2C_Write_Reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t cnt) { /* Initialize state machine */ Comm_State = TX_Reg_Index; I2C_Reg_Index = reg_addr; I2C_Reg_Data = reg_data; TXByteCtr = cnt; RXByteCtr = 0; /* Initialize slave address and interrupts */ UCB0I2CSA = dev_addr; IFG2 &= ~(UCB0TXIFG + UCB0RXIFG); // Clear any pending interrupts IE2 &= ~UCB0RXIE; // Disable RX interrupt IE2 |= UCB0TXIE; // Enable TX interrupt /* Start I2C communication */ TACTL |= MC_1 + TACLR; // Start timeout count (5ms) while (UCB0CTL1 & UCTXSTP) // Ensure stop condition got sent { if(Comm_State == Timeout) return Comm_State; } TACTL &= ~MC_1; // Stop timer TACTL |= MC_1 + TACLR; // Clear and restart timeout UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition __bis_SR_register(CPUOFF); // Enter LPM0 w/ interrupts TACTL &= ~MC_1; // Stop timer return Comm_State; } // Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A (void) { TACTL &= ~MC_1; // Disable timer Comm_State = Timeout; // Signal that a timeout occurred __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } /* Start / Stop / NACK ISR */ #pragma vector = USCIAB0RX_VECTOR __interrupt void USCIAB0RX_ISR(void) { if ((UCB0STAT & UCNACKIFG)) {//NACK received UCB0STAT &= ~UCNACKIFG; // Clear NACK Flags UCB0CTL1 |= UCTXSTP; // Send stop condition Comm_State = NACK; // Stop Communication and signal NACK IFG2 &= ~UCB0TXIFG; // Clear TXIFG USCI25 Workaround IE2 &= ~(UCB0TXIE + UCB0RXIE); // Disable RX and TX interrupts __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } } /* RX and TX ISR */ #pragma vector = USCIAB0TX_VECTOR __interrupt void USCIAB0TX_ISR(void) { static uint8_t temp; if(IFG2 & UCB0RXIFG) { //RX Interrupt //must read RXBUF first for USCI30 Workaround temp = UCB0RXBUF; if(RXByteCtr) { //Receive byte *I2C_Reg_Data++ = temp; RXByteCtr--; } if(RXByteCtr == 1) { // Stop needs to be sent at N-1 byte UCB0CTL1 |= UCTXSTP; } else if(RXByteCtr == 0) { // Received last byte, disable interrupts and return IE2 &= ~UCB0RXIE; Comm_State = Idle; __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } } else if (IFG2 & UCB0TXIFG) { //TX Interrupt /* Handle TXIFG based on state of communication */ switch(Comm_State) { case TX_Reg_Index: // Transmit register index to slave UCB0TXBUF = I2C_Reg_Index; // Send data byte if(RXByteCtr) Comm_State = Switch_to_RX; // Next state is to receive data else Comm_State = TX_Data; // Next state is to transmit data break; case Switch_to_RX: // Switch to receiver IE2 |= UCB0RXIE; // Enable RX interrupt IE2 &= ~UCB0TXIE; // Disable TX interrupt UCB0CTL1 &= ~UCTR; // Switch to receiver Comm_State = RX_Data; // State state is to receive data UCB0CTL1 |= UCTXSTT; // Send repeated start if(RXByteCtr == 1) { // Only receiving 1 byte, need to send stop immediately after start while((UCB0CTL1 & UCTXSTT)); UCB0CTL1 |= UCTXSTP; // Send stop condition } break; case TX_Data: // Transmit byte to slave if(TXByteCtr) { // Send byte UCB0TXBUF = *I2C_Reg_Data++; TXByteCtr--; } else { // Done transmitting data UCB0CTL1 |= UCTXSTP; // Send stop condition Comm_State = Idle; IE2 &= ~UCB0TXIE; // disable TX interrupt __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } break; default: __no_operation(); break; } } } /* Trap ISR for USCI29 Workaround */ #pragma vector= TRAPINT_VECTOR __interrupt void TRAPINT_ISR(void) { __no_operation(); // Add breakpoint }
Best regards,
Caleb Overbay
Hi,
Thank you for the answer. If I want to port the same code on the TI RF430FRL152H chip, will it work? I mean is the architecture and register set compatible across MSP430's ? If not, what do I need to change?
Hi,
No this code will not work on the RF430FRL152H because the MSP430G2553 uses an USCI module for communication and the RF430FRL152H uses the eUSCI module. You can find info on switching between the two modules here: Migrating from the USCI Module to the eUSCI Module
Best regards,
Caleb Overbay
**Attention** This is a public forum