Communicating to a part via I2C

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Anonymous
Not applicable

My board design uses a MMA8653FC Accelerometer 
I defined an I2C SCB to communicate with it 
Set up the SCL and SDA pins per my board schematic. The part SendStart routine returns no error so I assume the SCB is configured properly 
The part spec describes the single byte register read as follows 

5.8.1 Single-byte read 
1. The transmission of an 8-bit command begins on the falling edge of SCL. After the eight clock cycles are used to send the command, note that the data returned is sent with the MSB first after the data is received. Figure 10 shows the timing diagram for the accelerometer 8-bit I2C read operation. 
2. The Master (or MCU) transmits a start condition (ST) to the MMA8653FC [slave address (0x1D), with the R/W bit set to “0” for a write], and the MMA8653FC sends an acknowledgement. 
3. Next the Master (or MCU) transmits the address of the register to read, and the MMA8653FC sends an acknowledgement. 
4. The Master (or MCU) transmits a repeated start condition (SR) and then addresses the MMA8653FC (0x1D), with the R/W bit set to “1” for a read from the previously selected register. 
5. The Slave then acknowledges and transmits the data from the requested register. The Master does not acknowledge(NAK) the transmitted data, but transmits a stop condition to end the data transfer. 

I'm having trouble translating steps 4 and 5 into code that uses the I2C SCB 
#5 implies that the register must be read immediately after the repeated start is sent. It's not clear how to do this in SCB I2C. 
The Accel_I2CMasterReadByte issued after Accel_I2CMasterWriteBuf returns 0xFF because the check if master is active in Accel_I2CMasterReadByte fails : 
Here is a routine I use

   


byte IIC_RegRead(byte address, byte reg) 

byte b = 0; 
byte repeatStartAddr = (address << 1) | 0x01;  /* slave address 0x1D with the R/W bit set to “1” for a read from the previously selected register. */
status = Accel_I2CMasterSendStart(address,DIR_WRITE); 

if(status == Accel_I2C_MSTR_NO_ERROR) 

Accel_I2CMasterWriteByte(address); // Send IIC "Write" Address 
Accel_I2CMasterWriteByte(reg); // Send Register 

Accel_I2CMasterWriteBuf(address,&repeatStartAddr,1,Accel_I2C_MODE_REPEAT_START); 

   

----These fail
b = Accel_I2CMasterReadByte(GENERATE_ACK); // *** Dummy read: reads "IIC_ReadAddress" value *** 
b = Accel_I2CMasterReadByte(GENERATE_ACK);  /* read the value */
status = Accel_I2CMasterSendStop(); 

return b; 

Your help is much appreciated. 

0 Likes
1 Solution
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

You use

   

byte IIC_RegRead(byte address, byte reg)
{
    byte b = 0;
    byte repeatStartAddr = (address << 1) | 0x01;  /* slave address 0x1D with the R/W bit set to “1” for a read from the previously selected register. */

   


The shifting and ORing of the RnW bit is done by the component, no need for that code.

   

Try this:

   

byte IIC_RegRead(byte address, byte reg)
{
    byte b;
    uint32 status;
    status = Accel_I2CMasterSendStart(address,Accel_I2C_WRITE_XFER_MODE);    //    Request the bus
    if(status == Accel_I2C_MSTR_NO_ERROR)                                    //    When successful
    {
        status = Accel_I2CMasterWriteByte(reg);                                //    Send register to read from
        status = Accel_I2CMasterSendRestart(address,Accel_I2C_READ_XFER_MODE); // Restart to read mode
     
           b = Accel_I2CMasterReadByte(Accel_I2C_NAK_DATA);                    //    Read single byte
        status = Accel_I2CMasterSendStop();                                    //    Relinquish bus
    }
    return b;                                                                //    Return register value
    
}

   

 

   

Bob

View solution in original post

0 Likes
10 Replies
Anonymous
Not applicable

If you can post a minimum bundle of the workspace containing the code in question, that would help us out to debug it;

   

Here is an example project that uses the I2C SCB Master: http://www.cypress.com/documentation/code-examples/ce95363-i2c-master-using-serial-communication-blo...

0 Likes
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

Here is my project 

   

I will take a look at the sample

   

Thank you for help

0 Likes
Anonymous
Not applicable

OK, I updated my read code to use read/write buffer routines as in the sample.

   

I am getting data but not what I expect. The read is expected to return the device ID (0x5A)  but it does not.

   

Any ideas ? 

   

Thanks

   

byte buf[4];
byte addrWReadBitSet = (address << 1) | 0x01;  /* slave address 0x1D with the R/W bit set to “1” for a read from the previously selected register. */

   

 buf[0] = address;
 buf[1] = reg;

   

      //Write the address  and the register. Don't generate stop

   

      Accel_I2CMasterWriteBuf(address, buf, 2, Accel_I2C_MODE_NO_STOP);
    
        while (0u == (Accel_I2CMasterStatus() & Accel_I2C_MSTAT_WR_CMPLT))
        {
            /* Waits until master completes write transfer */
        }

   

    //Generate repeat start  and write the device address with the read bit set

   

      Accel_I2CMasterWriteBuf(address, &addrWReadBitSet, 1, Accel_I2C_MODE_REPEAT_START);
    
        while (0u == (Accel_I2CMasterStatus() & Accel_I2C_MSTAT_WR_CMPLT))
        {
            /* Waits until master completes write transfer */
        }

   

// now read the data

   

           memset(buf, 0, sizeof(buf));
        
          Accel_I2CMasterReadBuf(address, buf, sizeof(buf),Accel_I2C_MODE_COMPLETE_XFER); 
        
       // (void)  Accel_I2CMasterReadBuf(address, buf, sizeof(buf),Accel_I2C_MODE_COMPLETE_XFER); 

   

        while (0u == (Accel_I2CMasterStatus() & Accel_I2C_MSTAT_RD_CMPLT))
        {
            /* Waits until master complete read transfer */
        }

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

You use

   

byte IIC_RegRead(byte address, byte reg)
{
    byte b = 0;
    byte repeatStartAddr = (address << 1) | 0x01;  /* slave address 0x1D with the R/W bit set to “1” for a read from the previously selected register. */

   


The shifting and ORing of the RnW bit is done by the component, no need for that code.

   

Try this:

   

byte IIC_RegRead(byte address, byte reg)
{
    byte b;
    uint32 status;
    status = Accel_I2CMasterSendStart(address,Accel_I2C_WRITE_XFER_MODE);    //    Request the bus
    if(status == Accel_I2C_MSTR_NO_ERROR)                                    //    When successful
    {
        status = Accel_I2CMasterWriteByte(reg);                                //    Send register to read from
        status = Accel_I2CMasterSendRestart(address,Accel_I2C_READ_XFER_MODE); // Restart to read mode
     
           b = Accel_I2CMasterReadByte(Accel_I2C_NAK_DATA);                    //    Read single byte
        status = Accel_I2CMasterSendStop();                                    //    Relinquish bus
    }
    return b;                                                                //    Return register value
    
}

   

 

   

Bob

0 Likes
Anonymous
Not applicable

It worked! Thank you so much

   

On to the next problem 🙂

0 Likes
Anonymous
Not applicable

Ok, here is the next problem

   

I can now do a single byte read and get the device ID but write does not seem to work.

   

No error is returned from SCB routines but if I write a  non zero value and read it back a  zero is returned.

   

I realize this could be a board issue but looking at my write routine, does anything seem  incorrect  ?

   

 

   

void IIC_RegWrite(byte address, byte reg,byte val)
{
 
    Accel_I2CMasterSendStart(address,Accel_I2C_WRITE_XFER_MODE); 
                     
    Accel_I2CMasterWriteByte(address);     // Send IIC "Write" Address
                         
    Accel_I2CMasterWriteByte(reg);  // Send Register
                         
    Accel_I2CMasterWriteByte(val);// Send Value
                                
    Accel_I2CMasterSendStop();  // Send Stop
}

   

To test : The line below writes a 1 to the device control register 1 

   

IIC_RegWrite(SlaveAddressIIC, CTRL_REG1, 1);

   

Read it back:

   

 n = IIC_RegRead(SlaveAddressIIC, CTRL_REG1);

   

n  is zero.

   

 

   

Thanks 
Andy

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Delete the line

   

    Accel_I2CMasterWriteByte(address);     // Send IIC "Write" Address
 

   

You must not send the device address, this is done automatically by the SendStart() API

   

 

   

Bob

Anonymous
Not applicable

That was it

   

Now I can read data !

   

Thank you Bob!

   

On to the next problem 🙂

0 Likes
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

I am trying to do a very similar thing. I have a SI7021 humidity and temp sensor and a Pioneer BLE board and cannot get anything going. Can you share your solution? I am banging my head against a wall on this. I have the adafruit si7021 sensor and can read it just fine from a teensy running the basic example from here: https://github.com/adafruit/Adafruit_Si7021. I cannot seem to get the i2c bus on the pioneer board talking, though. When I scope it I get random cycles but no i2c comms. What am I missing?

0 Likes
Anonymous
Not applicable

Before you can read and write you need to call I2CM_I2CMasterSendStart and I2CM_I2CMasterSendStop when you're done

   

Check the return result of I2CM_I2CMasterSendStart - and only proceed with a read or write if there was no error

   

Another thing is that your code just calls I2CM_I2CMasterReadBuf to read the data. Maybe that's how your part works

   

In my case the part spec defines a process that has to be followed,i.e  to do a read, you first have to do a write to the  "read" register to tell the part which register you want to read from , then send a restart (I2CM_I2CMasterSendRestart())  and then do the read 

0 Likes