8 Replies Latest reply on Sep 20, 2019 12:58 PM by LoPo_4237926

    I2C: Cannot use MasterWriteByte

    LoPo_4237926

      I'm trying to implement a digital compass sensor using the QMC5883L (not HMC5883L). From the datasheet, the slave address is given as 0x0D.

       

      Here is what I have so far:

         

               UART_Start();

          I2C_Start();

         

          int status;

          char temp[20];

         

          UART_PutString("--- Starting ---\n");

         

          // Initiate communication

          status = I2C_MasterSendStart(QMC5883L_ADDR, I2C_WRITE_XFER_MODE);

         

          if (status == I2C_MSTR_NO_ERROR){

              // Point to data register at 0x00

              UART_PutString("No error. Proceeding...\n");

              status = I2C_MasterWriteByte(0x00);      // Problem occurs here! Cannot write

             

              while (status == I2C_MSTR_NOT_READY){

                  UART_PutString("ERROR: I2C_MSTR_NOT_READY\n");

                  status = I2C_MasterWriteByte(0x00);

                  I2C_MasterSendStop();

                  CyDelay(200);

              }

          }

          else{

              sprintf(temp, "ERROR: %d\n", status);

              UART_PutString(temp);

          }

       

       

          while(1)

          {

              // Initiate reading

              I2C_MasterSendStart(QMC5883L_ADDR, I2C_READ_XFER_MODE);

              uint16  x = I2C_MasterReadByte(I2C_ACK_DATA);

                      x = I2C_MasterReadByte(I2C_ACK_DATA) << 8 | x;

                     

              uint16  y = I2C_MasterReadByte(I2C_ACK_DATA);

                      y = I2C_MasterReadByte(I2C_ACK_DATA) << 8 | y;

                     

              sprintf(temp, "x: %d, y: %d\n", x, y);

          }

       

      The main problem so far is being unable to point to my data register at 0x00. It keeps throwing a I2C_MSTR_NOT_READY error. Not sure how to continue on as this is essentially how everyone is setting up their I2C connection :/

        • 1. Re: I2C: Cannot use MasterWriteByte
          MoTa_728816

          Hi,

           

          I tried your program using LM75B (address 0x48).

          SDA and SCL are pulled-up to 3.3V externally.

           

          And I modified some part of your program

          ===================

          #include "project.h"

          #include "stdio.h"

           

          // #define QMC5883L_ADDR 0x0D

          #define QMC5883L_ADDR 0x48 // I used LM75B

           

          int main(void)

          {

              CyGlobalIntEnable; /* Enable global interrupts. */

           

              UART_Start();

              I2C_Start();

            

              int status;

              char temp[20];

            

              UART_PutString("--- Starting ---\n");

            

              // Initiate communication

              status = I2C_MasterSendStart(QMC5883L_ADDR, I2C_WRITE_XFER_MODE);

            

              if (status == I2C_MSTR_NO_ERROR){

                  // Point to data register at 0x00

                  UART_PutString("No error. Proceeding...\n");

                  status = I2C_MasterWriteByte(0x00);      // Problem occurs here! Cannot write

                

                  while (status == I2C_MSTR_NOT_READY){

                      UART_PutString("ERROR: I2C_MSTR_NOT_READY\n");

                      status = I2C_MasterWriteByte(0x00);

                      I2C_MasterSendStop();

                      CyDelay(200);

                  }

                  I2C_MasterSendStop(); // <----- added

                  CyDelay(200);        // <----- added

              }

              else{

                  sprintf(temp, "ERROR: %d\n", status);

                  UART_PutString(temp);

              }

           

           

              while(1)

              {

                  // Initiate reading

                  I2C_MasterSendStart(QMC5883L_ADDR, I2C_READ_XFER_MODE);

                

                  uint16  x = I2C_MasterReadByte(I2C_ACK_DATA);

                          x = I2C_MasterReadByte(I2C_ACK_DATA) << 8 | x;

                        

                  uint16  y = I2C_MasterReadByte(I2C_ACK_DATA);

                          y = I2C_MasterReadByte(I2C_NAK_DATA) << 8 | y; // <---- modified for NAK

                  I2C_MasterSendStop() ;  // <----- added

                        

                  sprintf(temp, "x: %d, y: %d\n", x, y);

                  UART_PutString(temp);                                  // <----- added

                  CyDelay(1000) ;                                        // <----- added

              }

          }

          ===================

           

          Then the program seems to be working.

          Although I prefer to set PIN to open-drain drivings to low, your resistor pull-up also worked.

          (Please don't care about the value of x and y as we are reading Temp & Humidity Sensor ;-P)

           

          000-TeraTerm-log.JPG

           

          moto

          • 2. Re: I2C: Cannot use MasterWriteByte
            LoPo_4237926

            Hi, thanks for helping out. I saw that most of the code that you've added addresses the problems after pointing to the data register. However, the problem I have is that I can't point to my data register that I wish to read. Essentially, my code is getting stuck on my while loop for error checking:

             

            while (status == I2C_MSTR_NOT_READY){

             

            and I can't read my data register because of this.

             

            Perhaps a log of my UART terminal would give a better view of the problem:

             

            --- Starting ---

            No error. Proceeding...

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

            ERROR: I2C_MSTR_NOT_READY

             

            From what I've read, the HMC5883L chip uses the I2C address 0x1E but they've stopped manufacturing it and have licensed the manufacturing of the chip to another company, which produces the QMC5883L (the one I'm using) and uses the address 0x0D. I've tested it with the 0x1E address but to no avail. The initial I2C_MasterSendStart is successful (I2C_MSTR_NO_ERROR) which means connection to the I2C device at address 0x0D was successful. The problem only comes after that where I can't write to my slave to tell it which register I'd like to read later.

            • 3. Re: I2C: Cannot use MasterWriteByte
              MoTa_728816

              Hi,

               

              > "ERROR: I2C_MSTR_NOT_READY"

              In my experience this condition often happens when SDA is stuck at GND.

               

              From what I noticed in your project, you specified the pin types of

              SDA and SCL as resistive pull up. I wonder if you have external pull-up resistors.

               

              I recommend you (if you have not done so)

              (1) Add external pull-up resistors (2K ~ 10K) to SDA line and SCL line.

              001-connection.JPG

              (2) Change pin types to Open Drain Driving to Low

              002-SDA.JPG

              003-SCL.JPG

               

              moto

               

               

               

              • 4. Re: I2C: Cannot use MasterWriteByte
                SergiiV_71

                Few comments about original code:

                1. This while loop can't recover I2C because after you call I2C_MasterSendStop() the I2C transfer is completed - the Stop condition is sent on the bus. So you need to do Start condition again calling I2C_MasterSendStart

                 

                while (status == I2C_MSTR_NOT_READY){

                            UART_PutString("ERROR: I2C_MSTR_NOT_READY\n");

                            status = I2C_MasterWriteByte(0x00);

                            I2C_MasterSendStop();

                            CyDelay(200);

                        }

                 

                2. The reading should end with NACK so code should fixed

                uint16  y = I2C_MasterReadByte(I2C_ACK_DATA);

                        y = I2C_MasterReadByte(I2C_NAK_DATA) << 8 | y;

                        I2C_MasterSendStop()

                 

                3. Based on symptoms, it is not clear what is going on the I2C bus. Can you add the scope diagram of the I2C write transfer?

                 

                • 5. Re: I2C: Cannot use MasterWriteByte
                  LoPo_4237926

                  Hi, I've just tried using external pull-up resistors (2.2k) and it now gives me I2C_MSTR_ERR_LB_NAK on I2C_MasterSendStart. Seeing as I can't even connect to my sensor now, I don't think the issue is with the pull-up resistor. I'm tempted to use an external pull-up resistor in addition to setting the "Drive mode" of my pins to "Resistive pull up" as I'm running out of ideas :') (though I reckon I'd be reducing the overall pull-up resistance since they're in parallel and I doubt that's a good idea).

                  • 6. Re: I2C: Cannot use MasterWriteByte
                    LoPo_4237926

                    Hi, by scope diagram do you mean literally the waveform on an oscilloscope? If that's the case I'm afraid I don't have access to an oscilloscope with me until Monday.

                    • 7. Re: I2C: Cannot use MasterWriteByte
                      SergiiV_71

                      Yes. This is what I meant: waveform on an oscilloscope - it would help to define what is going on the I2C bus.

                       

                      The I2C_MasterSendStart needs the address as right justified 7-bit format, so please make sure that sensor provides I2C address in the same format or you need to adjust the address.

                       

                      You can scan try all addressed 0-128 and see which slave ACKs  the address (see pseudo code below):

                       

                      // Adjust per your I2C bus

                      #define NUMBER_OF_I2C_SLAVES_ON_THE_BUS (10U)

                       

                      uint8_t addr = 0;

                      uint8_t i = 0;

                      uint8_t slaves[NUMBER_OF_I2C_SLAVES_ON_THE_BUS];

                       

                      for (addr= 0; addr < 128; addr++)

                      {

                          status = I2C_MasterSendStart(addr, I2C_WRITE_XFER_MODE);

                          I2C_MasterSendStop();

                       

                          if (status != I2C_MSTR_ERR_LB_NAK)

                          {

                              // This slave respond to the master

                              slaves[i] = addr;

                              i++;

                          }

                       

                       

                          CyDelay(100);

                      }

                      • 8. Re: I2C: Cannot use MasterWriteByte
                        LoPo_4237926

                        Hi guys, after 4 hours of debugging and reading up different posts/arduino code online, I've finally gotten it to work!

                         

                        A few things I've learnt (this might be specific to just my QMC5883L as I accidentally shorted it for a short while (pun intended) earlier):

                        - To initialise communication between the PSoC and the sensor, you first need to write a 0x01 to register 0x0B, then a 0x1D (to setup mode, sample ratio, scale, etc.) to register 0x09. Obviously, you can change this value to something else depending on your needs but the datasheet recommends this mode (0x1D).

                        - For my case, I only needed x and y values. However, the sensor provides z values as well. From what I've observed, you need to read values up till z before NAK-ing it. For example,

                         

                            x = I2C_MasterReadByte(I2C_ACK_DATA);

                          x = I2C_MasterReadByte(I2C_ACK_DATA) << 8 | x;

                                   

                          y = I2C_MasterReadByte(I2C_ACK_DATA);

                          y = I2C_MasterReadByte(I2C_ACK_DATA) << 8 | y;

                         

                          z = I2C_MasterReadByte(I2C_ACK_DATA);

                          z = I2C_MasterReadByte(I2C_NAK_DATA) << 8 | z;

                         

                          works, but

                         

                          x = I2C_MasterReadByte(I2C_ACK_DATA);

                          x = I2C_MasterReadByte(I2C_ACK_DATA) << 8 | x;

                                   

                          y = I2C_MasterReadByte(I2C_ACK_DATA);

                          y = I2C_MasterReadByte(I2C_NAK_DATA) << 8 | y;

                         

                          does not.

                         

                        - Didn't really test this but I'm using "Open drain, drives low" with an initial high state on my pins with an external pull up resistor (I used 4.7k) as MoTa_728816 pointed out. Might work without external pull up resistors or even with the PSoC's resistive pull up drive mode.

                        - Communication with the QMC5883L has to be restarted after each NAK (aka pointing to the data register again).

                         

                        TLDR: Everything works now, thanks everyone for the help. I am eternally grateful as I'm new to PSoC and have never even touched I2C until these past couple of days while working on this sensor. For those who just want the code, its attached on this post. Note that the function that you should be calling if you plan on simply copy and pasting my code is getHeading(int x, y). Have fun : )