AnsweredAssumed Answered

ADXL345 and the MSP430F2013 I2C communication problem

Question asked by RobertAttaway on May 30, 2012
Latest reply on Jun 13, 2012 by jml

I have set up an MSP430F2013 as the master and the ADXL345 as the slave.  I am following the standard setup using the Device ID address of 0x1D.

 

My scenario is this

Generate a start on the I2C bus

Send Write command for 0x1D - Result address = 0x3A

Ack is received from slave

Send address for tap register - 0x1D

Ack is received from slave

Send data byte of 0x08

Ack is received from slave

Send stop

 

Next to test my write I want to read the same back

Generate a start on the I2C bus

Send Write command for 0x1D - Result address = 0x3A

Ack is received from slave

Send address for tap register - 0x1D

Ack is received from slave

Generate a start on the I2C bus

Send Read command for 0x1D - Result address = 0x3B

Ack Received from slave

Request the data byte

0xFF is received instead of 0x08

 

I2C communication stops and never finishes out the final I2C sequence defined on page 18 of the ADXL345 datasheet. My end goal is to configure the part using the adxl345_init_data array once I confirm that reading and writing to the part is occurring properly.

 

Thank you for any help.

 

My code is as follows:

 

// REF#1-783040831

#include  <msp430x20x2.h>   // Processor File

#include "adxl345.h"        // ADXL345 Prototypes

 

#define TAP_DUR        0x10        // 625 us/LSB, 0x10=10ms

#define THRESH_TAP    0x08        // 62.5mg/LSB, 0x08=0.5g

#define TAP_AXES    XL345_TAP_Z_ENABLE    // 0x2a: TAP_Z enable

#define XL345_FORMAT    XL345_FULL_RESOLUTION | XL345_DATA_JUST_RIGHT | XL345_RANGE_16G

 

void GenerateStart(void);

void WriteSlaveBusAddress(char, int);

void ReceiveAckNackFromSlave(void);

void SendAckNackToSlave(char);

void SendDataToSlave(char data);

void RequestDataFromSlave(void);

char ReceiveDataFromSlave(void);

void StopCondition(void);

void Setup_USI_Master_TX (void);

void Setup_USI_Master_RX (void);

void TransmitToSlave(void);

void ReceiveFromSlave(void);

 

int I2C_State = 0, Bytecount, Transmit = 0;     // State variable

char byteFromSlave = 0x00;

 

const unsigned char adxl345_init_data[] =

{

    XL345_THRESH_TAP, THRESH_TAP,        // 0x1d: 62.5mg/LSB, 0x08=0.5g

    XL345_OFSX, 0x00,                // 0x1e: 0xff

    XL345_OFSY, 0x00,                // 0x1f: 0x05

    XL345_OFSZ, 0x00,                // 0x20: 0xff

    XL345_DUR, TAP_DUR,                // 0x21: 625 us/LSB, 0x10=10ms

    XL345_LATENT, 0x00,                // 0x22: no double tap

    XL345_WINDOW, 0x00,                // 0x23: no double tap

    XL345_THRESH_ACT, 0x20,            // 0x24: 62.5mg/LSB, 0x20=2g

    XL345_THRESH_INACT, 0x03,            // 0x25: 62.5mg/LSB, 0x03=0.1875g

    XL345_TIME_INACT, 0x02,            // 0x26: 1s/LSB, 0x02=2s

    XL345_ACT_INACT_CTL, 0x00,            // 0x27: no activity interrupt

    XL345_THRESH_FF, 0x00,            // 0x28: no free fall

    XL345_TIME_FF, 0x00,            // 0x29: no free fall

    XL345_TAP_AXES, TAP_AXES,            // 0x2a: TAP_Z enable

    XL345_BW_RATE, XL345_RATE_100,        // 0x2c

    XL345_POWER_CTL, XL345_STANDBY,        // 0x2d: standby while changing int

    XL345_INT_ENABLE, 0x00        ,    // 0x2e: disable

    XL345_INT_MAP, 0x00,            // 0x2f: all interrupts to INT1

    XL345_DATA_FORMAT, XL345_FORMAT,            // 0x31

    XL345_FIFO_CTL, 0x00,            // 0x38

    XL345_INT_ENABLE, XL345_SINGLETAP,            // 0x2e: single tap enable

    XL345_POWER_CTL, XL345_MEASURE,        // 0x2d: measure

    0x00                    // end of configuration

};

char ackNacks[] = {0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0};

int ackNackIdx = 0;

void main(void)

{

  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog

  // USING 8MHZ AND CLOCK DIVIDER OF 0x20 to get 125kHz

  if (CALBC1_8MHZ ==0xFF || CALDCO_8MHZ == 0xFF)                                    

  { 

    while(1);                               // If calibration constants erased

                                            // do not load, trap CPU!!

  }

  BCSCTL1 = CALBC1_8MHZ;                    // Set DCO

  DCOCTL = CALDCO_8MHZ;

  P1OUT = 0xC0;                             // P1.6 & P1.7 Pullups, others to 0

  P1REN |= 0xC0;                            // P1.6 & P1.7 Pullups

  P1DIR = 0xFF;                             // Unused pins as outputs

  P2OUT = 0;

  P2DIR = 0xFF; 

 

  TransmitToSlave();

  // The results of transmit to slave never produce the right result because the

  // Ack from the Device Bus Address never returns. It is always a Nack

  ReceiveFromSlave();

  if(byteFromSlave == 0x08) // This is just a test for me to know that the write to the

                            // slave address worked. It hasn't yet

  {

    P1OUT |= 0x01;

    for(;;);

  }

}

 

void GenerateStart(void)

{

  USISRL = 0x00;                // Generate Start Condition...

  USICTL0 |= USIGE+USIOE;

  USICTL0 &= ~USIGE;

}

 

// this needs a parameter so on the receive the bus address can be a write on start

// and a read address on receive

void WriteSlaveBusAddress(char address, int READ_WRITE)

{

  USISRL =  (address << 1) + READ_WRITE;  // 0x91 Address is 0x48 << 1 bit + 1 for Read

  USICNT = (USICNT & 0xE0) + 0x08;        // Bit counter = 8, TX Address

  //USICTL1 &= ~USIIFG;

}

 

void ReceiveAckNackFromSlave(void)

{

  // Save USISRL Value here

  USICTL0 &= ~USIOE;                    // SDA = input

  USICNT |= 0x01;                       // Bit counter = 1, receive (N)Ack bit

  //USICTL1 &= ~USIIFG;

}

 

void SendAckNackToSlave(char ackNack)

{

  USICTL0 |= USIOE;                   // SDA = output

  USISRL = ackNack;                   // Send value to slave

  USICNT |= 0x01;                     // Bit counter = 1, receive (N)Ack bit

  //USICTL1 &= ~USIIFG;

}

 

void SendDataToSlave(char data)

{

  USICTL0 |= USIOE;                   // SDA = output

  USISRL = data;                      // Send the specific data byte to slave

  USICNT |= 0x08;                     // Bit counter = 1, receive (N)Ack bit

  //USICTL1 &= ~USIIFG;

}

 

void RequestDataFromSlave(void)

{

  //USICTL0 &= ~USIOE;                  // SDA = input

  USICNT |= 0x08;                     // Bit counter = 1, receive (N)Ack bit

  //USICTL1 &= ~USIIFG;

}

 

char ReceiveDataFromSlave(void)

{

  return USISRL;

}

 

void StopCondition(void)

{

  USISRL = 0x0FF;                   // USISRL = 1 to release SDA

  USICTL0 |= USIGE;                 // Transparent latch enabled

  USICTL0 &= ~(USIGE+USIOE);        // Latch/SDA output disabled

  //USICTL1 &= ~USIIFG;

}

 

void Setup_USI_Master_TX (void)

{

  _DINT();

  Bytecount = 0;

  Transmit = 1;

  USICTL0 = USIPE6+USIPE7+USIMST+USISWRST;  // Port & USI mode setup

  USICTL1 = USII2C+USIIE;                   // Enable I2C mode & USI interrupt

  USICKCTL = USIDIV_5+USISSEL_2+USICKPL;    // USI clk: SCL = SMCLK/128

  USICNT &= ~USI16B;                        // 8 bit words for I2C compatibility

  USICNT |= USIIFGCC;                       // Disable automatic clear control

  USICTL0 &= ~USILSB;                       // Use MSB

  USICTL0 &= ~USISWRST;                     // Enable USI

  USICTL1 &= ~USIIFG;                       // Clear pending flag

  _EINT();

}

 

 

void Setup_USI_Master_RX (void)

{

  _DINT();

  Bytecount = 0;

  Transmit = 0;

  USICTL0 = USIPE6+USIPE7+USIMST+USISWRST;  // Port & USI mode setup

  USICTL1 = USII2C+USIIE;                   // Enable I2C mode & USI interrupt

  USICKCTL = USIDIV_5+USISSEL_2+USICKPL;    // USI clks: SCL = SMCLK/32 about 200kHz

  USICNT &= ~USI16B;

  USICNT |= USIIFGCC;                       // Disable automatic clear control

  USICTL0 &= ~USILSB;

  USICTL0 &= ~USISWRST;                     // Enable USI

  USICTL1 &= ~USIIFG;                       // Clear pending flag

  _EINT();

 

}

 

/**************************************************************************************************

Service Routine should perform an entire write of one byte to the part

Then perform an entire read sequence reading the same address to which the data was sent

The result should be A = B

**************************************************************************************************/

 

#pragma vector = USI_VECTOR

__interrupt void USI_TXRX (void)

{

  if(Transmit==1)

  {

    switch(__even_in_range(I2C_State,12))

    {

      //Tasks to write to the slave

      //01 Generate a start command on the I2C bus

      case 0:

        GenerateStart(); // 0

        // Then the slave address for the device for which we will communicate

        WriteSlaveBusAddress(0x1d, 0);

        I2C_State = 2;

        break;

      //03 Receive the Ack/Nack from the I2C bus

      case 2:

        ReceiveAckNackFromSlave(); // 2

        I2C_State = 4;

        break;

      //04 Send register address

      case 4:

        SendDataToSlave(0x1d); // 4 - A register's address is just data

        I2C_State = 6;

        break;

      //05 Receive Ack/Nack from I2C bus

      case 6:

        ReceiveAckNackFromSlave();

        I2C_State = 8;

        break;

      //06 Send data to address sent in 4

      case 8:

        SendDataToSlave(0x08);

        I2C_State = 10;

        break;

      //07 Receive Ack/Nack from I2C bus

      case 10:

        ReceiveAckNackFromSlave();

        I2C_State = 12;

        break;

      //8.1 If no more data send stop command

      //8.2 If more data go to step 6 and send another data byte

      case 12:

        StopCondition();

        I2C_State = 0;

        break;

    }

  }

  else if(Transmit == 0)

  {

    switch(__even_in_range(I2C_State,18))

    {

      //Tasks to receive data from slave

      //01 Generate a start command on the I2C bus

      case 0:

        GenerateStart();

        WriteSlaveBusAddress(0x1d, 0);

        I2C_State = 2;

        break;

      //03 Receive the Ack/Nack from the I2C bus

      case 2:

        ReceiveAckNackFromSlave();

        I2C_State = 4;

        break;

      //04 Send register address

      case 4:

        SendDataToSlave(0x1d);

        I2C_State = 6;

        break;

      //05 Receive Ack/Nack from I2C bus

      case 6:

        ReceiveAckNackFromSlave();

        I2C_State = 8;

        break;

      //06 Generate a start command on the I2C bus

      case 8: 

        GenerateStart();

        WriteSlaveBusAddress(0x1d, 1); // Slave address and read bit

        I2C_State = 10;

        break;

      //08 Receive Ack/Nack from I2C bus

      case 10:

        ReceiveAckNackFromSlave();

        I2C_State = 12;

        break;

      //09 Receive data into USI register

      case 12:

        //USICTL0 |= USIGE; // We are going to try and force the latch transparency to pull SDA low

        //USICTL0 &= ~USIGE; // http://www.i2c-bus.org/analysing-obscure-problems/data-bytes-from-slave-are-0xff/

        RequestDataFromSlave();

        I2C_State = 14;

        break;

      case 14:

        // This code does everything I expect now up until this point

        // When the USI Interrupt Pulls the 8 bits from the slave address

        // instead of getting 0x08 I get 0xFF and then case 16 and 18 never

        // get called

        byteFromSlave = USISRL; //ReceiveDataFromSlave();

        I2C_State = 16;

        break;

      //10 Send Ack/Nack (Ack to read again Nack to stop)

      case 16:

        SendAckNackToSlave(0x01); // 0x00 is Ack and 0x01 is Nack

        I2C_State = 18;

        break;

      //10.1 If Ack go to 09

      //10.2 If Nack send final stop command

      case 18:

        StopCondition();

        I2C_State = 0;

        break;

    }

  }

    USICTL1 &= ~USIIFG; // This will be commented out of each function that the ISR calls

    /*

        After the service interrupt is used by Main for one read and one write the

        value in the byteFromSlave must be 0x08 because that is what we sent to

        that address during the write process

    */

}

void TransmitToSlave(void)

{

  Setup_USI_Master_TX();

  //P1OUT ^= 0x01;

  USICTL1 |= USIIFG;                      // Set flag and start communication

  //LPM0;                                   // CPU off, await USI interrupt

  for (long i = 0; i < 40000; i++);         // Delay between comm cycles

  //ackNacks[ackNackIdx++] = USISRL; // capture the last ackNack for the write sequence

}

void ReceiveFromSlave(void)

{

  Setup_USI_Master_RX();

  USICTL1 |= USIIFG;                        // Set flag and start communication

  //LPM0;                                     // CPU off, await USI interrupt

  for (long i = 0; i < 40000; i++);           // Delay between comm cycles

}

Outcomes