cancel
Showing results for 
Search instead for 
Did you mean: 

IIS2DLPC I2C Register Read

RSwai.1
Associate II

I am working with an IIS2DLPC. Using i2c I can write to and receive ACKs from the accelerometer as expected. I can also view this on my o'scope. However, when I request a read of any register I always get 0x0. I am using a PIC16F1503 microprocessor running at 16MHz. At this point all that is in my code is my i2c bit banging code. I assume I have some sort of timing issue on the read but I am not sure how to find it or determine my issue.

One thing to note, I have a function to wait for the bus to become idle. In this function there is a timeout in the event the clock is never released by the IIS2DLPC. This timeout seems to have no impact on the write operations. Again, I assume, because the clock is never released by the IIS2DLPC, I never receive the read.

I inherited this code from a prior project with a different accelerometer. However, everything looks right. Any help you can provide is appreciated.

void main(void) {
 
    OSCCON = 0x7A;    //16 Mhz       
 
    //RS this was not setting in the simulator
    while(!OSCSTATbits.HFIOFR){
        __delay_us(100);
    }
 
    CLRWDT();
 
    Init();
    
    I2c_Start();           // v51
    review[0] = I2c_Write(0x30);
    review[1] = I2c_Write(0x20);
    review[2] = I2c_Write(0x97);
    I2c_Stop();
       
    
    I2c_Start();           // v51
    review[3] = I2c_Write(0x30);
    review[4] = I2c_Write(0x25);
    review[5] = I2c_Write(0x44);
    I2c_Stop();
    
    while(1){  
    
    CLRWDT();   
 
    I2c_Start(); 
    review[6] = I2c_Write(0x30);
    review[7] = I2c_Write(0x0f);
    I2c_Repeat_Start();
    review[8] = I2c_Write(0x31);
    review[9] = I2c_Read(0);   // looking for low
    if(review[9]==0x00){
        review[10] = I2c_Read(0);
    }
    I2c_Stop(); 
    
    __delay_ms(2);
    }    
}
 
void Init(){       
 
    PWM4CON0bits.PWM4OE = 0;        
    APFCONbits.NCO1SEL = 1;    
    CLC2CONbits.LC2EN = 0;
    CLC2CONbits.LC2OE = 0;
    
    ANSELC = 0B00000000;    
    
    TRISCbits.TRISC0 = 1;
    TRISCbits.TRISC1 = 1; 
    
}
/******************************************************************************************/
// Initialize the I2C functions.
void I2c_Init()
{
   i2c_error = FALSE;
 
   // configure SCL and SDA pins as low (when configured as outputs)
   // use pins as open drain outputs
   i2c_sda = LOW;
   i2c_scl = LOW;
 
   // configure SCL and SDA pins as inputs
   i2c_scl_dir = INPUT;
   i2c_sda_dir = INPUT;
}
/******************************************************************************************/
 
/******************************************************************************************/
void I2c_Wait_For_Idle(void)
{
#define IDLE_TIMEOUT_COUNT   250
unsigned char temp;
 
   // wait for bus to be idle
   while ((i2c_scl == 0) && (temp < IDLE_TIMEOUT_COUNT))
      temp++;
 
   if (temp >= IDLE_TIMEOUT_COUNT)
      i2c_error = TRUE;
}
/******************************************************************************************/
 
/******************************************************************************************/
// Issue start condition
void I2c_Start(void)
{
   i2c_sda_dir = INPUT;    // make sure sda is input and recessive high
   i2c_scl_dir = INPUT;    // make sure scl is input and recessive high
 
   __delay_us(1);
 
   // wait for scl to be released by slave
   I2c_Wait_For_Idle();
 
   i2c_sda_dir = OUTPUT;   // move SDA low during SCL high (START CONDITION)
   i2c_sda = LOW;
   __delay_us(1);
 
   i2c_scl_dir = OUTPUT;   // move SCL low
   i2c_scl = LOW;
}
/******************************************************************************************/
 
 
/******************************************************************************************/
// Issue repeated start condition
void I2c_Repeat_Start(void)
{
   i2c_sda_dir = INPUT;    // make sure sda is input and recessive high
   i2c_scl_dir = INPUT;    // make sure scl is input and recessive high
 
   __delay_us(1);
 
   // wait for scl to be released by slave
   I2c_Wait_For_Idle();
 
   i2c_sda_dir = OUTPUT;   // move SDA low during SCL high (START CONDITION)
   i2c_sda = LOW;
   __delay_us(1);
 
   i2c_scl_dir = OUTPUT;   // move SCL low
   i2c_scl = LOW;
 
}
/******************************************************************************************/
 
 
/******************************************************************************************/
void I2c_Stop(void)
{
   i2c_sda_dir = OUTPUT;   // move SDA low
   i2c_sda = LOW;
   i2c_scl_dir = INPUT;    // make SCL high
 
   __delay_us(2);
 
   // wait for scl to be released by slave
   I2c_Wait_For_Idle();
 
   i2c_sda_dir = INPUT;    // move SDA high druing SCL high (STOP CONDITION)
 
   __delay_us(2);
}
/******************************************************************************************/
 
 
/******************************************************************************************/
// Read a byte.  acknowledge indicates whether or not to acknowledge the reception
unsigned char I2c_Read( unsigned char acknowledge )
{
unsigned char i;
unsigned char data;
 
	for (i=0; i<8; i++)					/* read 8 bit data */
	{
		data = (data << 1);				/* roll the bits one place to the left */
		i2c_scl_dir = INPUT;				/* make scl high, clock the data out on the rising edge */
 
		__delay_us(1);							/* 600 nsec < clock high min < infinity */
 
		data = (data + i2c_sda);		/* bring in the data bit */
		i2c_scl_dir = OUTPUT;         // make scl low
      i2c_scl = LOW;
 
		__delay_us(1);							/* 1300 nsec < clock low min < infinity */
	};
 
   // do we need to acknowledge the reception?
   if ( acknowledge )
   {
      // ack the reception
      i2c_sda_dir = OUTPUT;         // move sda low
      i2c_sda = LOW;
   }
   else
      // nack the reception
      i2c_sda_dir = INPUT;          // move sda high
 
	__delay_us(1);							/* maximum ee_sda fall time <= 300 nsec */
	i2c_scl_dir = INPUT;          // make scl high
 
	__delay_us(1);							/* 600 nsec < clock high min < infinity */
 
	i2c_scl_dir = OUTPUT;			/* make scl low */
	i2c_scl = LOW;			         /* make scl low */
 
	__delay_us(1);
 
   return( data );
};
/******************************************************************************************/
 
 
/******************************************************************************************/
// write a byte.  Returns Ack or Nack.
unsigned char I2c_Write( unsigned char data )
{
    unsigned char i;
 
    i2c_sda_dir = OUTPUT;   // move SDA low
    i2c_sda = LOW;
 
   for (i=0; i<8; i++)
   {
		if (data & BIT7)					/* (load the data) - mask all but MSB */
			i2c_sda_dir = INPUT;       // make sda high
      else
      {
         i2c_sda_dir = OUTPUT;      // make sda low
         i2c_sda = LOW;
      };
 
		data = (data << 1);				/* roll the bits one place to the left. */
 
		i2c_scl_dir = INPUT;				/* clock the data on the rising edge */
		__delay_us(1);							/* 600 nsec < clock high min < infinity */
		i2c_scl_dir = OUTPUT;
        i2c_scl = LOW;
		__delay_us(1);							/* 1300 nsec < clock low min < infinity */
	};
 
   // determine if slave ack'd or nack'd this byte
   i2c_sda_dir = INPUT;
   __delay_us(1);
   i2c_scl_dir = INPUT;
   __delay_us(1);
   if (i2c_sda)
      i = NACK_CODE;
   else
      i = ACK_CODE;
 
   i2c_scl_dir = OUTPUT;            // move scl low
   i2c_scl = LOW;
 
   return i;
}
/******************************************************************************************/
/* end of file */
//#endif

1 ACCEPTED SOLUTION

Accepted Solutions
RSwai.1
Associate II

Hi. Thanks for you replies. I believe I have found the issue. I had not cleared a pin and released it from PWM. The port is multifunction and the PWM function was a higher priority. I confirmed on my oscilloscope that I can receive 0x44 from the WHO_AM_I register.

View solution in original post

7 REPLIES 7
niccolò
ST Employee

Hi @RSwai.1​ ,

may I ask you why you are using handmade functions to write and read in i2c?

There could be some problems with those, but I'm not sure if I don't understand your code or if there is a bug somewhere.

To test it out, just try to read the register 0x0F before everything else. It is the WHO_AM_I, so it should return 0x44 for this device.

or maybe you can post the code with a little more comments to it, like the first write in lines 15-16-17, what are you writing and in which register?

I look forward to your reply.

RSwai.1
Associate II

Thank you for your reply. The first writes are writes to set control registers. If you notice in the while loop I attempt to read the WHO_AM_I register. All writes return as ACK but I never get anything back. I used your suggestion and attempted to read that register before I wrote anything to the device. It still returned nothing.

The i2c code was inherited from another project. It was working with another accelerometer so we chose to stick with it. However I am not converting to using the i2c engine in the PIC. I hope that the baud rate control offered in the PIC is my issue.

One question for you. If you will notice in the function i2c_wait_for_idle() there is a counter which times out in the event the clock line is not asserted low. I assumed my PIC timing was too fast for the IIS2DLPC so I removed the check temp < IDLE_TIMEOUT_COUNT in that while loop. When I do this the loop never kicks out. No write or read ever occurs. So the accelerometer "appears" to never release the clock. How can I ensure the accelerometer releases the clock.

RSwai.1
Associate II

This line in the above answer was a mistype. "However I am not converting to using the i2c engine in the PIC. "

In fact, i AM converting to using the i2c engine in the PIC.

Ok, let's focus on one thing at a time:

if you are sure that the I2c_Read function worked with the previous accelerometer, the problem should lie in hardware connections.

Can you provide me with the schematic of your board?

Regarding your other question:

in your code the i2c_scl variable is only set LOW (I imagine that is a 0), so it is normal for the loop to never exit the while, because the condition is always true.

Am I missing something?

AWood.2
Associate

Did you ensure that the CS pin is tied to Vcc? If not the device will be expecting SPI comms. I was using another ST MEMS sensor and had a similar issue - I had to assert I2C comms in the device initialisation. You don't need to do that in this case but the state of the CS pin is crucial.

RSwai.1
Associate II

Hi. Thanks for you replies. I believe I have found the issue. I had not cleared a pin and released it from PWM. The port is multifunction and the PWM function was a higher priority. I confirmed on my oscilloscope that I can receive 0x44 from the WHO_AM_I register.

nice to hear that you managed to find the problem, keep up the good work!