Hello all,
I am trying to figure out why my code doesn't work when I try to receive 2 or more byte from MPU-9250 slave device. receiving a single byte works properly and the Tx functionality also works.
If I comment out the:
if(i2c.length == 1)
{
UCB0CTL1 |= UCTXSTP;
}
in the Master Receive section of the ISR, and set UCTXSTP after reading the last byte, it works. According to the user guide, UCTXSTP is supposed to be set while the byte is being received.
The example codes also do this in a similar way to what I am doing. Any Ideas?
/*
* msp430_i2c.c
*
* Created on: Nov 1, 2019
* Author: Wayne
*
* Description: i2c driver for msp430g2553 using interrupts.
*/
#include <msp430.h>
#define SCL_PIN BIT6
#define SDA_PIN BIT7
typedef enum {
STATE_WAITING,
STATE_READING,
STATE_WRITING
} msp430_i2c_state;
typedef struct {
volatile msp430_i2c_state state;
unsigned char slave_reg; // First slave register.
unsigned char slave_reg_written; // 0 if slave register has not been written yet. */
unsigned char *data;
unsigned short length;
unsigned char enabled;
} msp430_i2c_info;
static msp430_i2c_info i2c = {
.enabled = 0
};
void i2c_enable()
{
/*Initialize USCI I2C Module*/
BCSCTL3 |= LFXT1S_2; // VLOCLK (~12 KHz) feeding ACLK (Select ACLK source to be internal VLO)
UCB0CTL1 |= UCSWRST | UCSSEL_1; // Hold USCI_B in reset for initialization (Set UCSWRST) & Select ACLK
UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC; // I2C Master Mode | I2C mode
UCB0BR0 = 8; // Set i2c frequency
UCB0BR1 = 0;
P1SEL |= SDA_PIN | SCL_PIN; // Configure ports
P1SEL2 |= SDA_PIN | SCL_PIN;
UCB0CTL1 &= ~UCSWRST; // Release USCI_B for operation (Clear UCSWRST)
IE2 |= UCB0TXIE | UCB0RXIE; // Enable interrupts (optional) via UCxRXIE and/or UCxTXIE
}
int i2c_write(unsigned char slv_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data)
{
/* Populate struct. */
i2c.state = STATE_WRITING;
i2c.slave_reg = reg_addr;
i2c.slave_reg_written = 0;
i2c.data = (unsigned char*)data;
i2c.length = length;
UCB0I2CSA = slv_addr; // write slave address to UCB0I2CSA register
UCB0CTL0 &= ~UCSLA10; // 7-Bit slave addressing mode
UCB0CTL1 |= UCTR + UCTXSTT; // set UCTR for tx mode + set UCTXSTT to generate start condition
while(i2c.state != STATE_WAITING)
{ /* Enter LPM0 w/ interrupts */
__bis_SR_register(CPUOFF + GIE);
i2c.state = STATE_WAITING;
}
return 0; // this return is pointless. need to return some err or something
}
int i2c_read(unsigned char slv_addr,unsigned char reg_addr,unsigned char length,unsigned char *data)
{
/* Populate struct. */
i2c.state = STATE_READING;
i2c.slave_reg = reg_addr;
i2c.slave_reg_written = 0;
i2c.data = data;
i2c.length = length;
UCB0I2CSA = slv_addr; // write slave address to UCB0I2CSA register
UCB0CTL0 &= ~UCSLA10; // 7-Bit slave addressing mode
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCTR | UCTXSTT; // I2C start condition
while(i2c.state != STATE_WAITING)
{ /* Enter LPM0 w/ interrupts */
__bis_SR_register(CPUOFF + GIE);
i2c.state = STATE_WAITING;
}
return 0;
}
#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
{
if(IFG2 & UCB0RXIFG) // Master Receive?
{
i2c.length--;
if(i2c.length)
{
*i2c.data++ = UCB0RXBUF;
if(i2c.length == 1)
{
UCB0CTL1 |= UCTXSTP;
}
}
else
{
*i2c.data = UCB0RXBUF;
// UCB0CTL1 |= UCTXSTP;
IFG2 &= ~UCB0TXIFG; // Clear TX interrupt flag
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
}
else if(IFG2 & UCB0TXIFG) // Master Transmit?
{
switch (i2c.state)
{
case STATE_WRITING:
if (!i2c.slave_reg_written)
{
i2c.slave_reg_written = 1; //
UCB0TXBUF = i2c.slave_reg; // Send slave address
}
else if (i2c.length) // Data to transmit?
{
char next = *i2c.data; //
i2c.data++; // increment byte
i2c.length--; // decrement the amount of data left to be sent
/* Writing to TXBUF must always be the final operation. */
UCB0TXBUF = next;
}
else // No More data to send?
{
UCB0CTL1 |= UCTXSTP; // Generate Stop condition.
IFG2 &= ~UCB0TXIFG; // Clear Tx interrupt flag
__bic_SR_register_on_exit(CPUOFF + GIE);
}
break;
case STATE_READING:
if (!i2c.slave_reg_written)
{
i2c.slave_reg_written = 1;
UCB0TXBUF = i2c.slave_reg;
}
else
{
/* Repeated start, switch to RX mode. */
UCB0CTL1 &= ~UCTR;
UCB0CTL1 |= UCTXSTT;
/* If single byte, prepare stop signal immediately. */
if (i2c.length == 1)
{
/* Well, not IMMEDIATELY. First we need to make sure
* the start signal got sent.
*/
while (UCB0CTL1 & UCTXSTT);
UCB0CTL1 |= UCTXSTP;
IFG2 &= ~UCB0TXIFG; // Clear TX interrupt flag
}
}
break;
}
}
}