11 Replies Latest reply on Mar 14, 2019 1:07 PM by jomac_4032611

    I2C data rate in PSoC Creator differs from real life

    jomac_4032611

      I have been testing I2C with my cy8ckit-042-ble-a. I started with this example: https://www.cypress.com/documentation/code-examples/ce211423-cy8ckit-042-ble-f-ram-i2c

      and got it working also with the actual I2C chip I need to communicate with. But I'm planing to use 400 kHz and I can't get that.

       

      If I use the example as is, the I2C component in PSoC Creator shows 100 kHz and I can measure 100 kHz with my oscilloscope as well. If I change IMO to 24 kHz and data rate to 400 kHz, PSoC Creator report "Actual data rate (kbps) 381". However my oscilloscope only sees 330 kHz on SCL.

       

      If I reduce the "Oversampling factor" 21->20, the reported actual data rate is 400 kbps. But on oscilloscope it's only 345 kHz.

       

      What causes this difference? How can I get real 400 kHz?

       

      I plan to use I2C during the BLE communication startup so that I can send fresh data on BLE. What low power BLE measures can't be used while I2C is used? Can IMO be shut down? Can sleep be used while I2C is transmitting or receiving as a master?

       

      When is the last time to update BLE data so that it will be output during this BLE wakeup and not the next one?

        • 1. Re: I2C data rate in PSoC Creator differs from real life
          EktaN_26

          Hello

          In order to get a data rate of 400 kHz the Fsys clk frequency must be between 7.82 MHz and 10 MHz (fast mode). To do this set the IMO to 8 MHz and divider to 1. This will set Fsys clk to 8 MHz.

          Data Rate = CLk frequency/ oversampling value.

          Set the oversampling value to 20 (high: 11, low: 9). This should give a data rate of 400 KHz.

          The accuracy of the internal/ external clk used and the oversamping value (in case of master) affects the data rate. Therefore the difference is observed.

           

          you can refer to page 53/211 of the link below:

          https://www.cypress.com/file/274551/download

           

          Master modes( master, multi-master) are not able to be a wake up source from sleep and deep sleep. This capability is only available for slave mode. Therefore sleep cannot be used if I2C is receiving or transmitting data as a master.

           

          Thanks

          Ekta

          • 2. Re: I2C data rate in PSoC Creator differs from real life
            jomac_4032611

            I can get 400 kHz in PSoC Creator with those settings, but I still get only 330-350 kHz scl on my oscilloscope. This can't be due to IMO inaccuracy, since then 100 kHz would be only ~85 kHz while I get 100 kHz on the same IMO setting. At 1000 kHz I get only 760 kHz so even bigger difference.

             

            From your link I found

            "Independent of chosen method the Component displays actual data rate for Master. This data rate might differ from the observed data rate on the bus due to the tR and tF time"

             

            Where do tR and tF come from? Measured while sampling? Then pullup values would change the rate. I will try.

             

             

            Doesn't I2C use interrupts internally? Any interrupt can be a wakeup source from sleep, but not from deep sleep. So maybe I can put the cpu to sleep untill the transfer is completed? There seems to be interrupt from ack, nack, stop and also from bus error and lost arbritation on master mode. That should be enough to wake up from sleep, when something is hapening on I2C.

            • 3. Re: I2C data rate in PSoC Creator differs from real life
              EktaN_26

              According to NXP I2C specifications tr (rise time) and tf (fall time) have particular maximum and minimum values for particular mode of operation. PSoC 4 also works in accordance with those specifications. You can refer to link below for those specifications.

               

              https://www.nxp.com/docs/en/user-guide/UM10204.pdf

               

              The pullup max. and min. value also depends upon rise time.

               

              I2C uses internal interrupts. Sleep mode is identical to Active from a peripheral point of view. No configuration changes are required in the Component or code before entering/exiting sleep. Any communication intended to the slave causes an interrupt to occur and leads to wakeup. Any master activity that involves an interrupt to occur leads to wakeup.

               

              Thanks

              Ekta

              • 4. Re: I2C data rate in PSoC Creator differs from real life
                jomac_4032611

                The frequency actually depended on the chosen pullups! With nothing on the I2C bus (just PSoC 4 and PCB) using 20 k pullups I got 305 kHz. With 10 k pullpups I got 331 kHz. Then activating the internal pullups (~5 k) I got 345 kHz (with or witout 10 k pullups). With 0.47 k pullups the waveform was really square and I got 397 kHz! (with our without internal pullups).

                 

                So I guess I have to experiment with pullups in order to find out a good balance for communication time vs. pullup power consumption for minimum power consumption.

                • 5. Re: I2C data rate in PSoC Creator differs from real life
                  jomac_4032611

                  I just was not expecting PSoC to actully measure the rise and fall times and adjust data rate accordingly. It seems to measure just the scl line, since I got 397 kHz with 10 k on sda and 0.47 k on scl. With P5.0 and P5.1 which have 2.2 k pullups and a FRAM on board + my own board on other PCB I got just 347 kHz with and 342 kHz without internal pullups.

                   

                  So maybe I can use 10 k on sda and ~1 k on scl? There will be just one slave on the bus and PCB track will be max 25 mm long. The slave datasheet does not tell pin capacitance. What would be a good guess for total capacitance on the scl?

                   

                  Shouldn't be more than 100 pF? Then 3 k pullup would be OK for 300 ns rise time required for 400 kHz. But PSoC seems to want more. With internal pullups I measure tR to be about 100 ns (30%->70%, 10%->90% is about 200 ns), but still I get only 345 kHz. Calculating backwards that should mean about 20 pF capacitance.

                   

                  So is PSoC actually measuring the resistance (pull down current) and assuming higher capacitance and then deciding the clock rate? The maximum 400 pF in the standard? Can this be somehow changed in the settings, since I will not have anything close to 400 pF and will likely get short enough rise times with 5-10 k.

                  • 6. Re: I2C data rate in PSoC Creator differs from real life
                    jomac_4032611

                    After some testing I was able to fool around 400 kHz using internal pullups. E.g. by setting IMO to 12 MHz, data rate to 1000 kHz and oversampling 15 low 9 high I get actual data rate 500 kHz in PSoC creator. With oscilloscope I get 424 kHz with internal pullups and 427 kHz with 2.2 k pullpups. The low time and high time are within I2C specs (1.3 and 0.9 us, specs require min 1.3 and 0.6 us).

                     

                    There is a complaint about clock frequency being out of range, but seems to work in practice. It even works with 48 MHz IMO when the actual data rate is 1000 kHz and real life shows 800 kHz, with low and high about 0.6 us. Despite the slave being only rated to work at 400 kHz and 1.3/0.6 us low and high.

                     

                    But in total this is not helping at all. I'm trying to keep the time needed to be awake as short as possible. However when I look at the oscilloscope what this command does:

                     

                    I2C_1_I2CMasterWriteBuf(0x60, (uint8 *) txBuffer, 3, I2C_1_I2C_MODE_COMPLETE_XFER);

                     

                    So it sends slaveaddres and three bytes from buffer. Should take just 100 us at 400 kHz. But it takes 132-135 us at both these settings. With IMO at 12 MHz and 427 kHz real life data rate there is first a burst of slave address, which takes about 22 us. But then SCL stays low and SDA high for 22 us. Why? ISR from ack? 240 clock cycles? Then there's a continuous burst for txBuffer, 3 bytes, which takes 64 us. But then again 22 us with SCL low and SDA high (short low to make stop at the end).

                     

                    I first thought slave is stretching, but changing IMO (and thus system) MHz to 48 MHz I get 794 kHz data rate. Now the same command becomes slave address burst 12 us, SCL low and SDA high 8 us, buffer burst 34 us and final SCL low and SDA high 8 us. So the SCL low SDA high time depends on system clock, not from slave.

                     

                    Any way to shorten that extra time?

                    • 7. Re: I2C data rate in PSoC Creator differs from real life
                      himam_31

                      Hello ,

                       

                      Do you have any other interrupts in your project? Can you give highest priority to the I2C interrupt and test this?

                       

                      Thanks,

                      Hima

                      • 8. Re: I2C data rate in PSoC Creator differs from real life
                        jomac_4032611

                        The actual project involves a BLE stack and a WDT timer with interrupts. But I test I2C in a separate project, which has just I2C interrupts. The I2C timing is equal in these two. There was a bit of difference, but I tracked it down to missing optimization parameter on the build command. Now I get the following:

                        IMO 12 MHz, no divers for I2C_1_SBCCLK or SYSCLK. I2C_1 set for 1000 kHz with manual oversampling control set to 24 (low 14, high 9). Actual data rate reported in PSoC Creator 4.2 500 kHz.

                         

                        The real lifel scl frequency on P5.1 with pullup on PCB and internal as well is 426 kHz.

                        Using:

                        I2C_1_I2CMasterWriteBuf(0x60, data, 2, I2C_1_I2C_MODE_COMPLETE_XFER)

                         

                        Measuring from scl this causes 21.2 us burst for slave address. Then scl is low for 18.4 us. 41.2 us burst for tho data bytes. Then scl is low for 18.6 us. This is the same all the time, so not caused by random interrupts etc.

                         

                        Then IMO to 48 MHz and I2C set to 400 kHz with manual oversampling set to 20 (low 11 high 9), which gives actual data rate 400 kHz. SBCCLK is 8 MHz.

                         

                        Now the real life scl frequency is 345 kHz. The slave address burst is 26.2 us, scl low 7.1 us, burst for data 51.6 us and scl low 7.1 us.

                         

                        Increasing SYSCLK 12->48 dropped the (unnecessary) gaps from 18.6 to 7.1 us. Not quite factor of four, but clearly related to SYSCLK. Clearly there is something wasting about 200 clock cycles.

                         

                        And there is even bigger gap after this I2C_1_I2CMasterWriteBuf command to the next one. 29.2 us at 12 and 12.2 us at 48 MHz. 29.2 us reduces to 26.8 us at 12 MHz, if I take the data setting and function calls away. Just repeat twice the same command with same data.

                         

                        300 clock cycles to start sending I2C buffer?

                        • 9. Re: I2C data rate in PSoC Creator differs from real life
                          jomac_4032611

                          I tried this software I2C: Software I2C Master Component for PSoC 3/4/5

                           

                          It was quite slow at 12 MHz (~90 kHz) and trying to make it as fast as possible I got to only 225 kHz. The slowness of changing pin value seems to be the problem. Just toggling ClrSCL, SetSDA, SetSCL in a loop gives only 289 kHz.

                           

                          Then I used tried direct register usage. It seems to work and is fast. No gaps between bytes. Is there a readily made system for this? Seems to be a lot of overhead from API. Though I don't have any error checking yet.

                           

                          Writing:

                          I2C_1_TX_FIFO_WR_REG=0xc0; //Put slave address to FIFO
                          I2C_1_I2C_MASTER_CMD_REG = I2C_1_I2C_MASTER_CMD_M_START; //Start + address
                          while(I2C_1_GET_TX_FIFO_SR_VALID);
                          TR_Write(1);
                          I2C_1_TX_FIFO_WR_REG=0x00; //Register address to  FIFO
                          while(I2C_1_GET_TX_FIFO_SR_VALID);
                          TR_Write(1);
                          I2C_1_TX_FIFO_WR_REG=0x00; //Register data to  FIFO
                          while(I2C_1_GET_TX_FIFO_SR_VALID);
                          TR_Write(1);
                          I2C_1_TX_FIFO_WR_REG=0x01; //Register data to  FIFO
                          while(I2C_1_GET_TX_FIFO_SR_VALID);
                          I2C_1_I2C_MASTER_GENERATE_STOP;
                          TR_Write(1);

                           

                          Reading

                           

                          rdata=(uint8 *)FRAMdata;
                          I2C_1_TX_FIFO_WR_REG=0xc1; //Put slave address to FIFO
                          I2C_1_I2C_MASTER_CMD_REG = I2C_1_I2C_MASTER_CMD_M_START; //Start + address
                          while(I2C_1_GET_TX_FIFO_SR_VALID);
                          TR_Write(0);
                          while(!I2C_1_GET_RX_FIFO_ENTRIES);
                          I2C_1_I2C_MASTER_GENERATE_ACK;
                          rdata[0]=(uint8)I2C_1_RX_FIFO_RD_REG;
                          TR_Write(1);
                          while(!I2C_1_GET_RX_FIFO_ENTRIES);
                          I2C_1_I2C_MASTER_GENERATE_ACK;
                          rdata[1]=(uint8)I2C_1_RX_FIFO_RD_REG;
                          TR_Write(0);
                          while(!I2C_1_GET_RX_FIFO_ENTRIES);
                          I2C_1_I2C_MASTER_GENERATE_ACK;
                          rdata[2]=(uint8)I2C_1_RX_FIFO_RD_REG;
                          TR_Write(1);
                          while(!I2C_1_GET_RX_FIFO_ENTRIES);
                          I2C_1_I2C_MASTER_GENERATE_ACK;
                          rdata[3]=(uint8)I2C_1_RX_FIFO_RD_REG;
                          TR_Write(0);
                          while(!I2C_1_GET_RX_FIFO_ENTRIES);
                          I2C_1_I2C_MASTER_GENERATE_ACK;
                          rdata[4]=(uint8)I2C_1_RX_FIFO_RD_REG;
                          TR_Write(1);
                          while(!I2C_1_GET_RX_FIFO_ENTRIES);
                          I2C_1_I2C_MASTER_GENERATE_STOP;
                          rdata[5]=(uint8)I2C_1_RX_FIFO_RD_REG;
                          TR_Write(0);

                           

                          TR_Write is just toggling a pin so I can see what's happening. It also gives a small gap, which seems to be needed by the slave. I don't know, if I2C_1_GET_TX_FIFO_SR_VALID and !I2C_1_GET_RX_FIFO_ENTRIES are good for knowing when the byte has been sent/received, but it seems to work.

                           

                          PS. How should code be copied to this forum. Copying from PSoC Creator forms a stupid table.

                          • 10. Re: I2C data rate in PSoC Creator differs from real life
                            himam_31

                            Hello,

                             

                            Can you attach the screenshot of the SCL and SDA lines showing delay in the SCB component?

                             

                            Note: Instead of attaching code you can attach zip of PSoC Creator Project archive.

                             

                            Thanks,

                            Hima

                            • 11. Re: I2C data rate in PSoC Creator differs from real life
                              jomac_4032611

                              I have studied I2C protocol, which I had not used previously (just SPI and some others). It seems this is usual and expected behavior of I2C hardware. It will start timing once it has measured the SCL line is at expected level and there seems to always be a delay, thus at higher data rates the actual rate will always be lower.

                               

                              I have now cyble-022001 on my custom board without pullups (using internal pullups) and the measured frequency is 425 kHz, when PSoC Creartor reports 500 kbps actual data rate. Since I'm using the registers directly (code somewhat modified from above) there is no extra delay. With API functions I always get a delay. I attach the screenshots while using my routines.

                               

                              First there is a picture of SCL kHz measurement. This is using my routines, but using API gives the same kHz. Rise time is within I2C specs.20190314_194120.jpg

                               

                              Here is writing to slave address 0x60 two bytes (0x03 and 0x00) using my functions, which have I2C interrupts disabled. Note it takes only 70 us

                              20190314_202105.jpg

                               

                              Here is one using API. Writing to the same slave address (0x60) byte 0x0b. Note the long pauses causing it to take 68 us for writing just one byte (+ slave address) so at the same time my routines write one extra byte.

                              20190314_205310.jpg

                               

                              Then scrolling a bit forward (0x0b still shows) there is a very long (47 us) pause before doing a restart and sending slave address again for reading the bytes in the 0x0b register of the I2C slave.

                              20190314_205359.jpg

                               

                              Note than in all these cases I'm reading/writing two I2C busses at the same time I2C_2 SCL and SDA are shown.

                               

                              My routine for writing (data is the data to be written, count is the number of data, 0xC0 is the slave address + write bit):

                               

                               

                              Before calling:

                                   I2C_1_Start(); // Start for usage

                                  I2C_2_Start(); // Start for usage

                                  I2C_1_DisableInt();  //Fast I2C must be run without I2C interrupts   

                                  I2C_2_DisableInt();

                               

                              Write function:

                               

                                  I2C_1_TX_FIFO_WR_REG=0xc0; //Put slave address to FIFO with write setting

                                  I2C_2_TX_FIFO_WR_REG=0xc0; //Put slave address to FIFO with write setting

                                  I2C_1_I2C_MASTER_CMD_REG = I2C_1_I2C_MASTER_CMD_M_START_ON_IDLE; //Start + address

                                  I2C_2_I2C_MASTER_CMD_REG = I2C_2_I2C_MASTER_CMD_M_START_ON_IDLE; //Start + address

                                  while(I2C_1_GET_TX_FIFO_SR_VALID || I2C_2_GET_TX_FIFO_SR_VALID);

                                  for(i=0;i<count;i++)

                                  {

                                      I2C_1_TX_FIFO_WR_REG=data[i]; //data to  FIFO

                                      I2C_2_TX_FIFO_WR_REG=data[i]; //data to  FIFO

                                      while(I2C_1_GET_TX_FIFO_SR_VALID || I2C_2_GET_TX_FIFO_SR_VALID);

                                  }

                                  while(I2C_1_scl_Read() || I2C_2_scl_Read()); //This is needed to get ack/nack

                                  CyDelayUs(1);

                                  I2C_1_I2C_MASTER_CMD_REG = I2C_1_I2C_MASTER_CMD_M_STOP; //Stop  

                                  I2C_2_I2C_MASTER_CMD_REG = I2C_2_I2C_MASTER_CMD_M_STOP; //Stop  

                               

                              API used in the API screenshots:

                               

                                  I2C_1_Start(); // Start for usage

                                  I2C_2_Start(); // Start for usage

                               

                                  /* Clear any previous status */

                                  I2C_1_I2CMasterClearStatus();

                                  I2C_2_I2CMasterClearStatus();

                                  /* I2C Write Command */

                                  status = I2C_1_I2CMasterWriteBuf(0x60, data, 1, I2C_1_I2C_MODE_NO_STOP);

                                  status |= I2C_2_I2CMasterWriteBuf(0x60, data, 1, I2C_2_I2C_MODE_NO_STOP);

                               

                                  for(;;)

                                  {

                                      if((I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT) &&

                                          (I2C_2_I2CMasterStatus() & I2C_2_I2C_MSTAT_WR_CMPLT))

                                      {

                                          /* Transfer complete. Check Master status to make sure that transfer

                                          completed without errors. */

                                          break;

                                      }

                                  }

                                 

                                  /* Clear any previous status */

                                  I2C_1_I2CMasterClearStatus();

                                  I2C_2_I2CMasterClearStatus();

                                 

                                  /* I2C Read Command */

                                  status = I2C_1_I2CMasterReadBuf(0x60,(uint8 *) ALS, 2, I2C_1_I2C_MODE_REPEAT_START );

                                  status |= I2C_2_I2CMasterReadBuf(0x60, (uint8 *) &ALS[1], 2, I2C_2_I2C_MODE_REPEAT_START );

                                  for(;;)

                                  {

                                      if((I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_RD_CMPLT) &&

                                      (I2C_2_I2CMasterStatus() & I2C_2_I2C_MSTAT_RD_CMPLT))

                                      {

                                          /* Transfer complete. Check Master status to make sure that transfer

                                          completed without errors. */

                                          break;

                                      }

                                  }