9 Replies Latest reply on May 7, 2017 7:43 AM by AndyMarder

    Communicating to a part via I2C

    AndyMarder

      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. 

        • 1. Re: Communicating to a part via I2C
          e.pratt_1639216

          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-block-scb-psoc-4

          • 2. Re: Communicating to a part via I2C
            AndyMarder

            Here is my project 

               

            I will take a look at the sample

               

            Thank you for help

            • 3. Re: Communicating to a part via I2C
              AndyMarder

              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 */
                      }

              • 4. Re: Communicating to a part via I2C
                user_1377889

                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

                • 5. Re: Communicating to a part via I2C
                  AndyMarder

                  It worked! Thank you so much

                     

                  On to the next problem :)

                  • 6. Re: Communicating to a part via I2C
                    AndyMarder

                    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

                    • 7. Re: Communicating to a part via I2C
                      user_1377889

                      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

                      1 of 1 people found this helpful
                      • 8. Re: Communicating to a part via I2C
                        AndyMarder

                        That was it

                           

                        Now I can read data !

                           

                        Thank you Bob!

                           

                        On to the next problem :)

                        • 9. Re: Communicating to a part via I2C
                          kgrider_2306876

                          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?

                          • 10. Re: Communicating to a part via I2C
                            AndyMarder

                            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