CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

i2c read specific data address
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

i2c read specific data address
PostPosted: Thu Nov 14, 2019 3:38 pm     Reply with quote

Hello,
I need to read 6 specific memory locations from a i2c 3axis accelerometer.
I ran the i2c scanner ( https://www.ccsinfo.com/forum/viewtopic.php?t=49713 )
and found addresses 0x32 (accelerometer) and 0x3C (magnetometer). It matches with the datasheet info.
https://www.st.com/resource/en/datasheet/lsm303agr.pdf
The data addresses I want to read are: 28, 29, 2A, 2B, 2C, 2D. (page 43)

I followed this guide, https://www.ccsinfo.com/forum/viewtopic.php?t=55088
and implemented the code for the first two bytes, but it doesn't work. I get "-1,0,255" in the serial monitor.
What am I doing wrong?

Code:


#include <16F1788.h>
#fuses INTRC_IO, PUT, NOWDT, NOLVP, NOPROTECT //INTRC_IO to be able to use A7 and A6 as GPIO
#device ADC=8
#use delay(clock=4000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)

void read_inclinometer();

void main(){
   setup_oscillator(OSC_4MHZ);
   delay_ms(500);

   printf("boot ... \n");
   
   while(true)
   {
      read_inclinometer();
      delay_ms(1000);
   }
}



#define incl_address 0x32 //32 or 3C

//---------------------------------------
void read_inclinometer(){
   int8 ACC_Data0, ACC_Data1, ACC_Data2, ACC_Data3, ACC_Data4, ACC_Data5;
   int16 x, y, z;
   int status;
   
   i2c_start();                    //start
   i2c_write(incl_address);   //send device address
   i2c_write(0x28);             //send address of the register I want to read
   i2c_start();                   //restart
   i2c_write(incl_address+1); //send device read address (device address+1)
   ACC_Data0 = i2c_read();   //Read bytes (NACK the last byte)
   i2c_stop();                      //stop
   
   
   i2c_start();
   status=i2c_write(incl_address); 
   i2c_write(0x29);
   i2c_start();
   i2c_write(incl_address+1);
   ACC_Data1 = i2c_read();
   i2c_stop();
   
   x = (int16)(ACC_Data1 << 8) | ACC_Data0;
   printf("%d,%d,%Ld\n", ACC_Data0, ACC_Data1, x );

   

}



temtronic



Joined: 01 Jul 2010
Posts: 9097
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Nov 14, 2019 4:29 pm     Reply with quote

I downloaded the sensor datasheet....
If you're trying to follow 'table22' of the device datasheet then you do not need the 2nd i2c_start() function.

Jay
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Nov 14, 2019 11:18 pm     Reply with quote

You have a problem in two places:
Quote:
ACC_Data0 = i2c_read(); //Read bytes (NACK the last byte)

ACC_Data1 = i2c_read();

You say this, but you don't do it. Look in the CCS manual to see how to
do it. Look in the i2c_read() section on page 315.
http://www.ccsinfo.com/downloads/ccs_c_manual.pdf
NACK means "No Ack", or "Do not Ack".
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 2:47 am     Reply with quote

Also, I2C devices generally support what is known as read/write 'burst'
operation. This device does. With this when you read a register, the
device automatically increments the address being accessed. So to read from
registers 28 & 29, you only need to send the address 28, then perform
two successive reads.
Code:

//---------------------------------------
void read_inclinometer(){
   int8 ACC_Data0, ACC_Data1, ACC_Data2, ACC_Data3, ACC_Data4, ACC_Data5;
   int16 x, y, z;
   int status;
   
   i2c_start();                    //start
   i2c_write(incl_address);   //send device address
   i2c_write(0x28);             //send address of the register I want to read
   i2c_start();                   //restart
   i2c_write(incl_address+1); //send device read address (device address+1)
   ACC_Data0 = i2c_read();   //Read bytes (NACK the last byte)
   //reads register 28
   ACC_Data1 = i2c_read(0); //This is where you need the NACK
   //This reads register 29.
   i2c_stop();
   
   x =make16(ACC_Data1,  ACC_Data0);
   printf("%d,%d,%Ld\n", ACC_Data0, ACC_Data1, x );
}


Also note using the 'Make16' operation. Read the manual for this. It
performs all the operations that were being done (moving the first
value into the MSB, and combining with the second value to return
an int16 result, as efficiently as possible. Your existing code would
not actually work. Bracket in the wrong place:

((int16)ACC_Data1 << 8)

It is ACC_Data1, that needs to be turned into an int16, _before_ the
rotation is performed, _not_ the result of the rotation. Doing it after
the rotation means the data has already been lost....
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 2:55 am     Reply with quote

Sorry, it needs to be "i2c_read(0);"
Thanks for pointing out Table 22 (page38). It says to send again the start condition.

I changed the code adding a bit of debugging printf. The i2c communication seems to work but the value I get isn't correct. I should get some unstable number. Confused

Code:

#include <16F1788.h>
#fuses INTRC_IO, PUT, NOWDT, NOLVP, NOPROTECT //INTRC_IO to be able to use A7 and A6 as GPIO
#device ADC=8
#use delay(clock=4000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)


void read_inclinometer();

void main(){
   setup_oscillator(OSC_4MHZ);
   delay_ms(500);
   printf("boot ... \n");
   
   
   while(true)
   {
      output_toggle(PIN_A7);
      read_inclinometer();
      delay_ms(1000);
   }
}

#define incl_address 0x32 //32 o 3C

//---------------------------------------
void read_inclinometer(){
   int8 ACC_Data0, ACC_Data1, ACC_Data2, ACC_Data3, ACC_Data4, ACC_Data5;
   int16 x, y, z;
   int1 status;
   
   printf("Read first byte ");
   i2c_start();               //start
   status=i2c_write(incl_address);   //send device address
   if(!status) printf("SAK "); else printf("problem ");
   status=i2c_write(0x28);           //send address of the register I want to read
   if(!status) printf("SAK "); else printf("problem ");
   i2c_start();               //restart
   status=i2c_write(incl_address+1); //send device read address (device address+1)
   if(!status) printf("SAK\n"); else printf("problem\n");
   ACC_Data0 = i2c_read(0);   //Read byte and NACK
   i2c_stop();                //stop
   
   printf("Read second byte ");
   i2c_start();
   status=i2c_write(incl_address); 
   if(!status) printf("SAK "); else printf("problem ");
   status=i2c_write(0x29);
   if(!status) printf("SAK "); else printf("problem ");
   i2c_start();
   status=i2c_write(incl_address+1);
   if(!status) printf("SAK\n"); else printf("problem\n");
   ACC_Data1 = i2c_read(0);
   i2c_stop();
   
   x = (int16)(ACC_Data1 << 8) | ACC_Data0;
   printf("%d,%d,%Ld\n\n", ACC_Data0, ACC_Data1, x );

}


result:
Code:
Read first byte SAK SAK SAK
Read second byte SAK SAK SAK
0,0,0
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 3:16 am     Reply with quote

Thanks Ttelmah.
So, I made the code more readable, and read the other registers in burst mode.

Code:
void read_inclinometer(){
   int8 ACC_Data0, ACC_Data1, ACC_Data2, ACC_Data3, ACC_Data4, ACC_Data5;
   int16 x, y, z;

   
   i2c_start();               //start
   i2c_write(incl_address);   //send device address
   i2c_write(0x28);           //send address of the register I want to read
   i2c_start();               //restart
   i2c_write(incl_address+1); //send device read address (device address+1)
   ACC_Data0 = i2c_read();   //Read register 28 and ACK
   ACC_Data1 = i2c_read(0); //Read register 29 and NACK
   i2c_stop();                //stop
   
   x =make16(ACC_Data1,  ACC_Data0);
   //x = ((int16)ACC_Data1 << 8) | ACC_Data0;
   printf("%d,%d,", ACC_Data0, ACC_Data1);

   i2c_start();               //start
   i2c_write(incl_address);   //send device address
   i2c_write(0x2A);           //send address of the register I want to read
   i2c_start();               //restart
   i2c_write(incl_address+1); //send device read address (device address+1)
   ACC_Data2 = i2c_read();   //Read register 2A and ACK
   ACC_Data3 = i2c_read(); //Read register 2B and ACK
   ACC_Data4 = i2c_read(); //Read register 2C and ACK
   ACC_Data5 = i2c_read(0); //Read register 2D and NACK
   i2c_stop();       
   
   printf("%d,%d,%d,%d\n\n", ACC_Data2, ACC_Data3, ACC_Data4, ACC_Data5);
}


Still I get 0 for all of them :(
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 3:26 am     Reply with quote

What value are your pull-up resistors?.
By default the #use will be selecting fast mode at 400KHz. This is only
supported with a reasonably low pull-up. Depends on the length of the
wires and traces, and what connectors are present. Reading '0' is what
typically happens if the pull-ups are not small enough.
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 4:05 am     Reply with quote

10 kOhm. The problem persist with 1 kOhm.
The circuit is on a breadboard, with 20 cm dupont cable to the LSM303 adapter board. Power is 3.3V from the ICD-U64 programmer.
Regarding #use, I tried 100 kHz and SLOW, but it didn't solve the problem.

I am going to try the device with a ESP8266+Arduino.
I wonder if the device needs to be configured on some other register. I used this chip before with ESP8266+Arduino but I don't remember having changed any configuration.

UPDATE
The module works ok with ESP8266+Arduino.
The Arduino i2c scanner gives me address 0x19 (it is a 7 bit address, 0011001, which is 0x32 if adding a zero at the end 00110010).
temtronic



Joined: 01 Jul 2010
Posts: 9097
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 6:34 am     Reply with quote

comments...
At 3.3 volts, I usually use 3k3 pullups..
Does the 'module' have it's own pullups ? Some do, some don't.
You may need to turn off any other peripherals that can use the I2C pins.

Since PCM P's I2C scanner program works, the hardware should be OK.
Since the Ardunio code works, I'd print it out and compare to the PIC code. Check each line and see what the diference is, if any.

also
I re-read the datasheet and read that SR is really same as ST, arrgh... I wrongly assumed SR meant Slave Read, not Start Read. I thought ST meant STart.....

Jay
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 7:32 am     Reply with quote

The board with the inclinometer has no pullups.
The ESP module does not have the pullups in the schematic, but I didn't put external pullups and it worked, so I think it enabled internal pullups automatically on the i2c pins.

From the PIC16F1788 datasheet page 9 (pin allocation table)
http://ww1.microchip.com/downloads/en/DeviceDoc/40001675C.pdf
I see that RC3 RC4 are shared with PSMC, SCK SDI. How do I disable those peripherals?
RB6 RB7 are alternative pins for i2c, but I am using them for ICSP and serial port, so I think the configuration is ok there.



The Arduino code:
0x19 is the inclinometer address (7 bit) which correspond to the 0x32 (8 bit) I used for the PIC.
Code:
int ACC_Data0, ACC_Data1, ACC_Data2, ACC_Data3, ACC_Data4, ACC_Data5;
int x, y, z;
const float alpha = 0.15;
float fx = 0;
float fy = 0;
float fz = 0;

#include <Wire.h>

void setup() {

  Wire.begin();
  Serial.begin(9600);
 
  Wire.beginTransmission(0x19); // Set accel
  Wire.write(0x20);             // CTRL_REG1_A register
  Wire.write(0x47);             // 50 Hz, normal power, all 3 axis enabled
  Wire.endTransmission();

  Wire.beginTransmission(0x19); // Set accel
  Wire.write(0x23);             // CTRL_REG4_A register
  Wire.write(0x08);             // continuous update, littleendian, 2g, high resolution, 4-wire spi
  Wire.endTransmission();
 

}

void loop() {
  Wire.beginTransmission(0x19);
  Wire.write(0x28);
  Wire.endTransmission();
  Wire.requestFrom(0x19, (byte)1);
  ACC_Data0 = Wire.read();
 
  Wire.beginTransmission(0x19);
  Wire.write(0x29);
  Wire.endTransmission();
  Wire.requestFrom(0x19, (byte)1);
  ACC_Data1 = Wire.read();

  Wire.beginTransmission(0x19);
  Wire.write(0x2A);
  Wire.endTransmission();
  Wire.requestFrom(0x19, (byte)1);
  ACC_Data2 = Wire.read();
 
  Wire.beginTransmission(0x19);
  Wire.write(0x2B);
  Wire.endTransmission();
  Wire.requestFrom(0x19, (byte)1);
  ACC_Data3 = Wire.read();

  Wire.beginTransmission(0x19);
  Wire.write(0x2C);
  Wire.endTransmission();
  Wire.requestFrom(0x19, (byte)1);
  ACC_Data4 = Wire.read();
 
  Wire.beginTransmission(0x19);
  Wire.write(0x2D);
  Wire.endTransmission();
  Wire.requestFrom(0x19, (byte)1);
  ACC_Data5 = Wire.read();

  x = (int16_t)(ACC_Data1 << 8) | ACC_Data0;
  y = (int16_t)(ACC_Data3 << 8) | ACC_Data2;
  z = (int16_t)(ACC_Data5 << 8) | ACC_Data4;

  Serial.print(x); Serial.print(","); Serial.print(y); Serial.print(","); Serial.println(z);
 

  delay(250);

}
temtronic



Joined: 01 Jul 2010
Posts: 9097
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 7:52 am     Reply with quote

Good that you checked about the pullups as it eliminates one possible problem.

The Ardunio code has a 'setup' function and the CCS one does not. That could be the problem. I see 'SPI' in the setup commands.. It is possible that the 'default' parameters of the sensor are not allowing it to function as an I2C device. Maybe the default is SPI mode !
You'll need to read the datasheet, make a list of the 'setup' registers and how YOU want them to be, then add code to do so.

As a general comment, never assume 'default' values are correct for YOUR application, always code them yourself.

Jay
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 9:51 am     Reply with quote

Also, wouldn't it be nice to tell us what inclinometer chip you are using ?
If you bought a board, give a link to the website for it.
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 11:50 am     Reply with quote

The chip datasheet is linked in the first post ;)
The evaluation board https://www.st.com/en/evaluation-tools/steval-mki172v1.html

I hooked up the scope to SCL SDA, and found that the i2c signals for the ESP improved a lot adding 3,3k pull ups. It worked without anyway.
With the scope connected to the computer (Rigol DK1052E + PulseView) I decoded the communication. Seems to match the C instructions. Any simple way of attaching images here?

Aaaaand it seems temtronic spotted the problem! Very Happy
It was something I tested earlier today (running the ESP program without the initialization code), but I DIDN'T POWER OFF AND ON THE INCLINOMETER, which kept the initialization.
So I had the impression that it was not needed.

In the ESP program I now removed the setup code and I get 0,0 as I get from the PIC.
So, I am going to implement the chip initialization in the PIC and let you know.

BTW: first time I used it, very cool and handy https://sigrok.org/wiki/Main_Page
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 5:29 pm     Reply with quote

I start to see varying numbers once I implemented the setup: Laughing
Code:
  i2c_start();               //start
   i2c_write(incl_address);   //send device address
   i2c_write(0x20);           //send address of the register I want to write
   i2c_write(0x47);           //send data
   i2c_stop();                //stop
   
   i2c_start();               //start
   i2c_write(incl_address);   //send device address
   i2c_write(0x23);           //send address of the register I want to write
   i2c_write(0x08);           //send data
   i2c_stop();                //stop

However the burst reading isn't working. On the reads after the first one I get again the value of the first read. It looks like the inclinometer isn't incrementing the pointer to the next register.
Code:
 i2c_start();               //start
      i2c_write(incl_address);   //send device address
      i2c_write(0x28);           //send address of the register I want to read
      i2c_start();               //restart
      i2c_write(incl_address+1); //send device read address (device address+1)
      ACC_Data0 = i2c_read();    //Read register 28 and ACK
      ACC_Data1 = i2c_read(0);    //Read register 29 and NACK
      i2c_stop();                //stop


The decoding of the i2c data seems to match the datasheet specification, but the second reply is always identical to the first. Shocked

temtronic



Joined: 01 Jul 2010
Posts: 9097
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Nov 15, 2019 6:32 pm     Reply with quote

I think you want to increment the registers within the device NOT the device address.

this line of your code...
Quote:
i2c_write(incl_address+1); //send device read address (device address+1)

incl_address is really the address of the physical device (the chip), you want to increment the REGISTERS within the device

Jay
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group