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.

MSP430FR5969: Read multiple bytes of data i2c with repeated start and without interrupts

Part Number: MSP430FR5969

I build two functions for reading and writing a single byte using the eusci i2c interface.
I was not able to find any examples that did not use interrupts so I build my own.
I did a lot of debugging with a logic analyser, but they are working fine now.

oid i2c_init(){

    P1SEL1 |= BIT6 | BIT7;                  // configure I2C pins
    P1SEL0 &= ~(BIT6 | BIT6);               // configure I2C pins

    // I2C default uses SMCLK
    UCB0CTL1 |= UCSWRST;                    // put eUSCI_B in reset state
    UCB0CTLW0 |= UCMODE_3 | UCMST | UCSYNC; // I2C, master, sync
    UCB0BRW = 0x000A;                       // baud rate = SMCLK / 10 = 100khz
    UCB0CTL1 &= ~UCSWRST;                   // eUSCI_B in operational state
}

void i2c_write(uint8_t slv_addr, uint8_t reg_addr, uint8_t data){

    while(UCB0STAT & UCBBUSY);

    UCB0I2CSA = slv_addr;                   // set slave address
    UCB0CTLW0 |= UCTR | UCTXSTT;            // transmitter mode and START condition.

    while (UCB0CTLW0 & UCTXSTT);
    UCB0TXBUF = reg_addr;
    while(!(UCB0IFG & UCTXIFG0));
    UCB0TXBUF = data;
    while(!(UCB0IFG & UCTXIFG0));

    UCB0CTLW0 |= UCTXSTP;
    while(UCB0CTLW0 & UCTXSTP);             // wait for stop
}

uint8_t i2c_read(uint8_t slv_addr, uint8_t reg_addr){

    uint8_t data = 0;

    while(UCB0STAT & UCBBUSY);
    UCB0I2CSA = slv_addr;                   // set slave address
    UCB0CTLW0 |= UCTR | UCTXSTT;            // transmitter mode and START condition.

    while(UCB0CTLW0 & UCTXSTT);
    UCB0TXBUF = reg_addr;
    while(!(UCB0IFG & UCTXIFG0));

    UCB0CTLW0 &= ~UCTR;                     // receiver mode
    UCB0CTLW0 |= UCTXSTT;                   // START condition

    while(UCB0CTLW0 & UCTXSTT);             // make sure start has been cleared
    UCB0CTLW0 |= UCTXSTP;                   // STOP condition
    while(!(UCB0IFG & UCRXIFG0));
    data = UCB0RXBUF;

    while(UCB0CTLW0 & UCTXSTP);

    return data;
}

I found debugging the single read and write that all the "instructions" should be just in the right order for it to work stable.
However, some sensors require you to read multiple registers without a intermediate stop.
Because if you do try to read them byte by byte, after reading the first one, new data is stored in the registers.

To my understanding:
- Both a stop and repeated start automatically send a NACK instead of a ACK
- while(UCB0STAT & UCBBUSY); waits as long as the bus is busy. ( So this should not be checked with a repeated start? )

How should I modify my single read function, for it to be able to read multiple bytes?
I tried some things but when should I send a repeated start? in the same spot as stop? 

Ideally I want to have a function as this:

uint8_t i2c_read_multi(uint8_t slv_addr, uint8_t reg_addr, uint8_t l, uint8_t arr*){}

l for the number of bytes to read and arr* a pointer to an array of size l

  • I figured it out myself.

    No restart needs to  be send when you want the read a new byte.

    Every sensor will auto increment the register adres after a read.

    So reading can just be done in a Loop:

    void i2c_read_multi(uint8_t slv_addr, uint8_t reg_addr, uint8_t l, uint8_t *arr){
    
       uint8_t i;
    
       while(UCB0STAT & UCBBUSY);
    
       UCB0I2CSA = slv_addr;                   // set slave address
    
       UCB0CTLW0 |= UCTR | UCTXSTT;            // transmitter mode and START condition.
    
       while(UCB0CTLW0 & UCTXSTT);
    
       UCB0TXBUF = reg_addr;
    
       while(!(UCB0IFG & UCTXIFG0));
    
       UCB0CTLW0 &= ~UCTR;                     // receiver mode
    
       UCB0CTLW0 |= UCTXSTT;                   // START condition
    
       while(UCB0CTLW0 & UCTXSTT);             // make sure start has been cleared
    
       for (i = 0; i < l; i++) {
    
           while(!(UCB0IFG & UCRXIFG0));
    
           if(i == l - 1){
    
               UCB0CTLW0 |= UCTXSTP;           // STOP condition
    
           }
    
           arr[i] = UCB0RXBUF;
    
       }
    
       while(UCB0CTLW0 & UCTXSTP);
    
    }

  • I'm glad to see that you've found the answer to your problem, thank you for following up and posting the solution.

    Regards,
    Ryan

**Attention** This is a public forum