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.

Another G2553 I2C thread, cant ACK of G2553 slave

FIXED


had wrong understanding of the interrupt routines of the G255. new code can be found in a post below.


Hello everyone,


I am currently trying to interface an F5529 and a G2553 vie I2C. What i try to do is:

F5529 adresses G2553, G2553 sends some bytes.

I have previously used the F5529 with an RPi, the F5529 being the slave and sending data to the RPi on request. So i have basic knowledge on how I2C works. Now, that im trying to interface the G2553, im getting really frustrated. I read a lot of forums and anything else i could find for 2 days now. Seems like im not the only person with problems but i couldnt find any helpful information.

What i do so far:

  • 10K pullups on both SDA and SCL
  • Pullups work, checked with an oscilloscope
  • Master F5529 sends the adress, slave G2553 does not generate an ACK
  • I am using P1.6 and 1.7 on the G2553 (Launchpad)
  • P1.6 jumper for LED removed

Other things i noted:

  • G2553, RX and TX share an interrupt in I2C mode
  • P1SEL+P1SEL2, UCB0IE+IE2 <- 2 registers

Things im not sure about:

  • When is the TX IFG set? after receiveing the adress?
  • What is a good way of recognizing an I2C master has adressed me and wants data? Can i just wait for a TX interrupt?
  • If the answer to the question above is yes: Could I wait until the G2553 is adressed, enable the STPIE, TX data and then, on recognizing the STP signal,  reset my pointer that i use for sending data? (Either in the interrupt or by leaving low power mode)
  • Maybe i dont need to enable the RX interrupt (im currently thinking of waiting for a defined Byte to be received to then react by sending TX data), but can just use TX and start sending immediately (with no RX Byte i check, as there is only one option of what to send anyways)
  • If iwant to uise a switch-case in the ISRs to check the Interrupt Flag, how to i find out what the different cases (0, 2, 4 ..) stand for?

My code (i tried to minimalize it) for the initialization:


#define OWN_ADRESS        0x35

int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT P1DIR = BIT0; // Set P1.0 to output, rest of P1 to input P1IE = BIT4; // activate interrupts (for counting Geiger impulses) TACTL = TASSEL_1 + MC_2 + TAIE + ID_2; // ACLK, contmode, interrupt, divide by 8; I2C_Setup(OWN_ADRESS); // setup I2C, 0x3B slave adress _bis_SR_register(LPM3_bits + GIE); // Enter LPM3, enable interrupts } void I2C_Setup(short adress){     P1DIR |= BIT6 | BIT7;
    P1REN =  BIT6 | BIT7;                    // enable pullups
    P1SEL |= BIT6 | BIT7;                    // I2C Lanes (P1.6-SCL und P1.7-SDA) (Auf peripheriegerät stellen)
    P1SEL2|= BIT6 + BIT7;                   // Assign I2C pins to USCI_B0
UCB0CTL1 |= UCSWRST; // USCI im Reset Status festhalten UCB0CTL0 = UCMODE_3 + UCSYNC; // slave + I2C + synchroner Modus UCB0BR0 = 0; // recommendation of a forum member UCB0BR1 = 0; // recommendation of a forum member UCB0I2COA = adress; // Set I2C Slave Adress (0x35 as set with the RPi group) and enable its usage UCB0CTL1 &= ~UCSWRST; // release from reset USCI IE2 = UCB0RXIE; // Enable RX interrupt }

I got a timer Interrupt working and a Port 1 interrupt for pin 1.3.

My Interrupt routines for

#define DATA_REQUEST    0x55

// USCI_B0 Data ISR
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCIAB0TX_VECTOR
__interrupt void RX_ISR_I2C(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCIAB0TX_VECTOR))) RX_ISR_I2C (void)
#else
#error Compiler not supported!
#endif
{
    unsigned short in = UCB0RXBUF;
    int TicksPerMinute;
    if(in == DATA_REQUEST){
          P1OUT ^= BIT0;
          UCB0I2CIE = UCSTPIE + UCSTTIE;        // Transmission started, activate stop interrupts
          IE2 |= UCB0TXIE;                                         // Enable TX interrupts
    }
    UCB0TXBUF = *sendDataPointer++;
}

// USCI_B0 State ISR
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCIAB0RX_VECTOR))) USCIAB0RX_ISR (void)
#else
#error Compiler not supported!
#endif
{
    UCB0STAT &= ~(UCSTPIFG + UCSTTIFG + UCNACKIFG);
    IE2 |= UCB0TXIE;
}

Master Code looks like this (im using a self written I2C library that works well with differenc sensors).

I2C0_SetSlaveAdress simply sets UCB0SCA to its parameter, I2C0_SendAndRead sends start+adress+write, writes a single byte, then repstart, reads n bytes and stores them at the given pointer location (and the following registers).This works well

#define GEIGER_ADRESS	0x35
#define GEIGER_GET_DATA	0x55

int Geiger_GetData(){
	unsigned short ReceivedValues[2] = {0, 0};
	I2C0_SetSlaveAdress(GEIGER_ADRESS);						// set 0x35 (adress of Geiger MSP)
	I2C0_SendAndRead(GEIGER_GET_DATA, 2, ReceivedValues);		// Send comand 0x55 (request data)
	return ((int)(ReceivedValues[0]<<8) + ReceivedValues[1]);
}

  • Ok Im finding some of the answers myself and will post all of them in here in case someone else is having the same questions.

    What is a good way of recognizing an I2C master has adressed me and wants data?

    • When a START condition is detected on the bus, the USCI module will receive the transmitted address
      and compare it against its own address stored in UCBxI2COA. The UCSTTIFG flag is set when address
      received matches the USCI slave address.

  • 10k pull-ups might be too weak. It depends on line capacitance and length and number of peers on the bus. It might work, it may fail. Especially when using higher clock speeds, 4k7 or even 1k are required.
    On the G2553, there are multiple functions on port pins, besides PxSEL, there’s also PxSEL2. See the port pin logic in the device datasheet. There’s a truth table.
    For UCA0 and UCB0 (more exactly, first and second UART), there are bits in IEx and IFGx registers for RX and TX (been there since 1x family). However, USCIB has more interrupts than RX and TX, and there’s also USCIA1 or USCIB2 on some devices. So additional registers have been introduced. On 5x family, this has been streamlined (each module has its own IFG and IE registers)
    Yes, 0n 2x family in I2C mode, RX and TX interrupts share one ISR (depending on transfer direction, only one of them can ever happen at the same time), while the status interrupts go to the other one. Also, USCIA and USCIB share the same RX and TX ISRs. On 5x family, all interrupts of one USCI share the same ISR, but USCIA and USCIB have separate ones.
    The TXIFG bit is set as soon as the USCI has received a start byte with its own address and the R/W bit set.
    As you figured out yourself, you can act on the UCSTTIFG interrupt. You should also check the UCTR bit to know whether it is a read or write request.
    When UCTR is set, you have plenty of time to decide whether to write to TXBUF or set UCTXNACK to refuse the connection. If UCTR is clear, the USCI has already ACK’d the transfer and started receiving the first byte. If you set UCNACKIFG, transfer will end after the byte has been received.
    If you don’t enable RX interrupt and don’t set UCNACKIFG after receiving a start byte, the bus may stall, as the master doesn’t care for you not wanting to receive anything. And since in this case the USCI will automatically ACK the start byte, the but would be stalled. You shouldn’t assume that the master will only ask what you support. Implement fail-safe reaction for everything.
    The 2x family USCI does not have an IV register. On 5x family, the USCI does. Reading the IV register returns the pending interrupt with the highest priority, and at the same time clears the corresponding IFG bit. In I2C mode, this means the USCI will continue reading incoming data even though the last received byte hasn’t been read from RXBUF. Unless you are sure this won’t happen (e.g. with 1-byte only transfers, or fast MCLK and slow ISC clock), you shouldn’t use this register or at least first check RXIFG before going into the switch. The values are listed in the register description in the users guide.
  • Thanks for your detailed answer!

    I had managed to get the communication working yesterday evening, should have closed the thread then maybe. On my way home I sorted all the information i got and established the communication successfully, including the checks of UCTR etc. If anyone is intersted in this, here is the source code:

    void I2C_Setup(short adress){
    //	P1DIR |= BIT6 | BIT7;
    //	P1REN &=  ~(BIT6 | BIT7);					// enable pullups
    	  P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
    	  P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
    	  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
    	  UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
    	  UCB0I2COA = 0x35;                         // Own Address is 035h
    	  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
    	  UCB0I2CIE = UCSTPIE + UCSTTIE;           // Enable STT and STP interrupt
    	  IE2 = UCB0TXIE;                          // Enable TX interrupt
    }
    
    // USCI_B0 Data ISR
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void USCIAB0TX_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCIAB0TX_VECTOR))) USCIAB0TX_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    	UCB0TXBUF = *sendDataPointer++;
    }
    
    // USCI_B0 State ISR
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCIAB0RX_VECTOR
    __interrupt void USCIAB0RX_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCIAB0RX_VECTOR))) USCIAB0RX_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    	if	((UCB0STAT & UCSTTIFG) && (UCB0STAT & UCTR)) // Device was adressed, master wants to write
    	{
    		UCB0CTL1 |= UCTXNACK;	// send NACK --> Don't read from Bus, make master send stop condition
    	}
    	else if (UCB0STAT & UCSTPIFG) //
    	{
    		 __bic_SR_register_on_exit(CPUOFF);	// run main() loop to reset everything
    	}
    	UCB0STAT &= ~(UCSTPIFG + UCSTTIFG);
    }

    On solving this, i headed into another problem tho. COmmunication is only successful in debug mode. If you dont mind having a look at this, i created another thread.

**Attention** This is a public forum