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.
Hi,
I've written my own code for reading and writing a single byte over i2c (without using interrupts), which work fine.
For some sensors you need to read multiple registers one after another without a STOP, otherwise the data get's discarded.
I'm having the following problem:
- The first time (after programming) when a multi read is executed it also stores a additional byte in the RXBUF.
So let's say I want to read register 0x01 until 0x04 than it also stores the byte from 0x05 in the RXBUF.
This results in my code reading the "wrong" byte after the first multi read.
The next single read will read the wrong "old" byte still stored in the RXBUF.
So all the reads after the first multi read are wrong in case of a single read and shifted starting with a wrong byte in case of a multi read.
I fixed this by reading out the RXBUF a additional time after a successful STOP.
This seems really strange to me and without the "fix" only the first multi read a additional byte is read.
Is there a fault in my code or is this fix really necessary???
#include <msp430.h> #include <stdint.h> #include "eusci_b0_i2c.h" void 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; } void i2c_read_multi(uint8_t slv_addr, uint8_t reg_addr, uint8_t l, uint8_t *arr){ uint8_t i; volatile uint8_t del; 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); //The first time using multi read, an extra register byte is returned. //This makes the successive reads, read "old" data. if(UCB0IFG & UCRXIFG0){ del = UCB0RXBUF; } }
Section 26.3.5.2.2 of the User's Guide says:
A STOP condition is either generated by the automatic STOP generation or by setting the UCTXSTP bit. The next byte received from the slave is followed by a NACK and a STOP condition. This NACK occurs immediately if the eUSCI_B module is currently waiting for UCBxRXBUF to be read.
In theory, your code looks correct. But in practice, it appears you're setting the TXSTP bit too late, so try moving it before the RXIFG wait.
Or just use the automatic STOP generation.
**Attention** This is a public forum