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 CCS Technical Support

6DOF imu (ADXL345 and itg3200)

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
perseo963



Joined: 02 Feb 2013
Posts: 4
Location: colombia

View user's profile Send private message Send e-mail

6DOF imu (ADXL345 and itg3200)
PostPosted: Tue Feb 19, 2013 9:19 pm     Reply with quote

Hi all, I'm working with a 6DOF imu (ADXL345 and itg3200).
The goal is to find the degree of inclination for each axis, and display it on an LCD. I designed this code based on the ADXL345 accelerometer datasheet, but found no reply from accelerometer. As the pic also works at 5v I made an interface using mosfet as indicated by Philips for attaching different voltage levels 5v to 3.3v in i2c line.
Thank you for your help.
Code:

#include <16f877a.h>
#fuses hs,NOWDT,NOPROTECT,PUT,NOBROWNOUT,NOLVP
#use delay(clock=20M)
#use i2c(MASTER, SDA=PIN_c4, SCL=PIN_c3, force_hw)

#include <stdlib.h>
#include <math.h>
#include <lcd.c>

#define WRITE_DEV_ADD 0xA6
#define READ_DEV_ADD 0xA7
#define FIFO_MODE 0x38
#define BW_RATE 0x2C
#define POWER_CTL 0x2D
#define DATA_FORMAT 0x31
#define DATAX0 0x32 
#define DATAX1 0x33
#define DATAY0 0x34
#define DATAY1 0x35
#define DATAZ0 0x36
#define DATAZ1 0x37

#DEFINE INT_ENABLE 0X2E

char x0,x1,y0,y1,z0,z1;
long int x,y,z;

void inicializa_acelerometro()
{
i2c_start();
i2c_write(WRITE_DEV_ADD);
i2c_write(DATA_FORMAT);
i2c_write(0x0B);
I2C_STOP();

i2c_start();
i2c_write(WRITE_DEV_ADD);
i2c_write(POWER_CTL);
i2c_write(0x08);
I2C_STOP();

i2c_start();
i2c_write(WRITE_DEV_ADD);
i2c_write(INT_ENABLE);
i2c_write(0x80);
I2C_STOP();
}

void main ()
{
enable_interrupts(int_rda);
enable_interrupts(GLOBAL);

WHILE(true)
{
 lcd_init();
 inicializa_acelerometro();
 
 i2c_start();
 i2c_write(WRITE_DEV_ADD);
 i2c_write(0x32);
 i2c_start();
 i2c_read(READ_DEV_ADD);
  x0=i2c_read(0x32);
 i2c_read(0);
 i2c_stop();
   
 i2c_start();
 i2c_write(WRITE_DEV_ADD);
 i2c_write(0x32);
 i2c_start();
 i2c_read(READ_DEV_ADD);
  x0=i2c_read(0x32);
 i2c_read(0);
 i2c_stop();
 
 i2c_start();
 i2c_write(WRITE_DEV_ADD);
 i2c_write(0x33);
 i2c_start();
 i2c_read(READ_DEV_ADD);
  x1=i2c_read(0x33);
 i2c_read(0);
 i2c_stop();
 
 i2c_start();
 i2c_write(WRITE_DEV_ADD);
 i2c_write(0x34);
 i2c_start();
 i2c_read(READ_DEV_ADD);
  y0=i2c_read(0x34);
 i2c_read(0);
 i2c_stop();
 
 i2c_start();
 i2c_write(WRITE_DEV_ADD);
 i2c_write(0x35);
 i2c_start();
 i2c_read(READ_DEV_ADD);
  y1=i2c_read(0x35);
 i2c_read(0);
 i2c_stop();
 
 i2c_start();
 i2c_write(WRITE_DEV_ADD);
 i2c_write(0x36);
 i2c_start();
 i2c_read(READ_DEV_ADD);
  z0=i2c_read(0x36);
 i2c_read(0);
 i2c_stop();
 
 i2c_start();
 i2c_write(WRITE_DEV_ADD);
 i2c_write(0x37);
 i2c_start();
 i2c_read(READ_DEV_ADD);
  z1=i2c_read(0x37);
 i2c_read(0);
 i2c_stop();
 
   //////////concatenar datos adxl345/////////////
      x=((x1)<<8)|x0 ;
      y=((y1)<<8)|y0;
      z=((z1)<<8)|z0;
      ////////////////////////////////////////////////
 printf(lcd_putc," \f %ld %ld %ld   ",x,y,z);
}
}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Feb 19, 2013 10:56 pm     Reply with quote

Run this program on your board. See if it detects your two i2c devices.
If it doesn't, work on your hardware until it does work:
http://www.ccsinfo.com/forum/viewtopic.php?t=49713
Ttelmah



Joined: 11 Mar 2010
Posts: 19539

View user's profile Send private message

PostPosted: Wed Feb 20, 2013 2:00 am     Reply with quote

As a comment, I presume you are referring to AN97055 for the level shifting?. This does work, _but_ selection of the MOSFET is critical. 90% of MOSFET's have too high a Vgs for this circuit. You need ones with Vgs <2v, and quite low gate capacitances (<100pF). Unless these parameters are met, the circuit will be unreliable.
As PCM programmer says, use the tester first, and if this doesn't work, then you know you have a hardware problem.
Also, what values are you using for the Rp resistors?. Remember these need to be significantly _lower_ on the 3.3v part of the bus.

Best Wishes
perseo963



Joined: 02 Feb 2013
Posts: 4
Location: colombia

View user's profile Send private message Send e-mail

perseo963
PostPosted: Wed Feb 20, 2013 5:02 pm     Reply with quote

hello
I tested the code I found in the link you sent me PCM programmer and not hardware problem, it works perfectly, I visualize the direction of the gyroscope (DO) and accelerometer (A6).
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Feb 20, 2013 7:08 pm     Reply with quote

Quote:
i2c_start();
i2c_write(WRITE_DEV_ADD);
i2c_write(0x32);
i2c_start();
i2c_read(READ_DEV_ADD);
x0=i2c_read(0x32);
i2c_read(0);
i2c_stop();

These read routines are incorrect. Delete all your existing axis data
read routines (there are several of them in your code).

You're supposed to read all 6 bytes in one pass, according to the
ADXL345 data sheet. Your axis read code should look like this:
Code:

i2c_start();
i2c_write(WRITE_DEV_ADD);
i2c_write(0x32);    // x0 data register address
i2c_start();
i2c_write(READ_DEV_ADD);
x0 = i2c_read(); 
x1 = i2c_read(); 
y0 = i2c_read(); 
y1 = i2c_read(); 
z0 = i2c_read(); 
z1 = i2c_read(0);  //  NACK on last read
i2c_stop();

Each of those axis data variables should be declared as 'int8'.

When you combine the msb and lsb bytes for each axis, use the
CCS function make16(). Put the result in an 'int16' variable. It's easier.


Also you have these two lines in your code:
Quote:
enable_interrupts(int_rda);
enable_interrupts(GLOBAL);

But I don't see any #int_rda routine. Delete those two lines.
Don't enable interrupts without an interrupt handler routine.
perseo963



Joined: 02 Feb 2013
Posts: 4
Location: colombia

View user's profile Send private message Send e-mail

perseo963
PostPosted: Tue Mar 12, 2013 9:28 pm     Reply with quote

Hello again, from the code that I built with his help I received sensor data, on 3 axes. I tested for concatenation of several ways, with make16 and bit shifting. My ultimate goal is to find the angle sensor inclination from the horizontal. However I do not understand because the sensor does not give negative readings for certain positions as stated in the datasheet and so to get a total reading 360 degrees in all three axes, I studied and I used AN-1057 formulations, but the output values are not consistent.
Appreciate your help.
Code:

#include <16f877a.h>
#fuses XT,NOWDT,NOPROTECT,PUT,NOBROWNOUT,NOLVP
#use delay(clock=4M)
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <lcd.c>

//#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7,  bits=8, parity=N)
#use i2c(MASTER, SDA=PIN_c4, SCL=PIN_c3,FORCE_HW)
#define ACCEL_WRITE_ADDR  0XA6//0xA6 //0x53//0x3a
#define ACCEL_READ_ADDR  0XA7//0xA7  //0x53//0x3b
#define ACCEL_DATA_ADDR 0x32
#define ACCEL_PWRCTRL_ADDR 0x2d
#define ACCEL_MEASURE_MODE 0x08
 
int i,accel_data[6],j;
int16 x,y,z,x2,y2,z2;
//unsigned int off_x=6,off_y=9,off_z=232;
float x1,y1,z1,R;
float teta=0, gama=0,fi=0;
 
 
void main ()
{
   // initialize
   for (i = 0; i < 6; i++) {
      accel_data[i] = 0;
   }

   while(true) {
      lcd_init();

      // Tell the accelerometer to wake up
      i2c_start();
      i2c_write(ACCEL_WRITE_ADDR);
      i2c_write(0x31);
      i2c_write(0x0B);
      i2c_stop();

      i2c_start();
      i2c_write(ACCEL_WRITE_ADDR);
      i2c_write(ACCEL_PWRCTRL_ADDR);
      i2c_write(ACCEL_MEASURE_MODE);
      i2c_stop();

      i2c_start();
      i2c_write(ACCEL_WRITE_ADDR);
      i2c_write(0X2E);
      i2c_write(0X80);
      i2c_stop();
//////////////////////////////////////////////

      // Read data from the accel
      i2c_start();
      i2c_write(ACCEL_WRITE_ADDR);
      i2c_write(ACCEL_DATA_ADDR);
      i2c_start();
      i2c_write(ACCEL_READ_ADDR);
      accel_data[0] = i2c_read(); //x0
      accel_data[1] = i2c_read(); //x1
      accel_data[2] = i2c_read(); //y0
      accel_data[3] = i2c_read(); //y1
      accel_data[4] = i2c_read(); //z0
      accel_data[5] = i2c_read(); // z1, NACK on last read
      i2c_read(0);
      i2c_stop();

      //////////concatenar datos adxl345/////////////
     // x=((accel_data[1])<<8)+ accel_data[0] ;
      //y=((accel_data[3])<<8)+ accel_data[2];
      //z=((accel_data[5])<<8)+ accel_data[4];
     
      x=make16((accel_data[1]),(accel_data[0]));
      y=make16((accel_data[3]),(accel_data[2]));
      z=make16((accel_data[5]),(accel_data[4]));
     
      x1=(float)x/256;
      y1=(float)y/256;
      z1=(float)z/256;
      ////////////////////////////////////////////////
      R=sqrt((x1*x1)+(y1*y1)+(z1*z1));
     
      teta=acos(x1/R);
      teta=teta * 57,296;
     
      gama=acos(y1/R);
      gama=gama*57,296;
     
      fi=acos(z1/R);
      fi=fi*57,296;
           
   printf(lcd_putc,"\f %.2f %.2f %.2f ",x1,y1,z1);
   printf(lcd_putc,"\n %f %f %f ",teta,gama,fi);
           
   }
}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Mar 12, 2013 10:03 pm     Reply with quote

Quote:
accel_data[0] = i2c_read(); //x0
accel_data[1] = i2c_read(); //x1
accel_data[2] = i2c_read(); //y0
accel_data[3] = i2c_read(); //y1
accel_data[4] = i2c_read(); //z0
accel_data[5] = i2c_read(); // z1, NACK on last read
i2c_read(0);

i2c_stop();

This is not correct. I showed you sample code in an earlier post, but
you ignored it. Why ? To do a NACK on the last read, means that
you should do it on the last read operation. It doesn't mean that you
should stick in another i2c_read line and do the NACK there.
Look at my example code and follow it.


Quote:
However I do not understand because the sensor does not give negative readings

But the variables that you declared, such as these:
Code:
int16 x,y,z,x2,y2,z2;

are unsigned. Look in the CCS manual. CCS has a lot of quirks.
It's different, in many ways, from "normal" C. One of the differences is
that the CCS data types, such as int8, int16, and int32 are unsigned.
If you want signed variables, then you have to tell the compiler. Example:
Code:
signed int16 x,y,z,x2,y2,z2;

This is in the CCS manual, on page 45 (page 57 in the Acrobat reader):
http://www.ccsinfo.com/downloads/ccs_c_manual.pdf
The manual says:
Quote:
unsigned - Data is always positive. This is the default data type if not specified.
perseo963



Joined: 02 Feb 2013
Posts: 4
Location: colombia

View user's profile Send private message Send e-mail

PostPosted: Wed Apr 10, 2013 8:31 pm     Reply with quote

Hello again,
Thanks for your help PCM,
I need to get the angle between the horizontal and the floor but I have not been able... I add the code to see if there is an error, I read the AN1057 and followed their instructions but did not work ...
Many thanks again.
Code:

#include <16f877a.h>
#fuses xt,NOWDT,NOPROTECT,PUT,NOBROWNOUT,NOLVP
#use delay(clock=4M)
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <lcd.c>
#use i2c(MASTER, SDA=PIN_c4, SCL=PIN_c3)

#define ACCEL_WRITE_ADDR 0XA6//0xA6 //0x53//0x3a
#define ACCEL_READ_ADDR 0XA7//0xA7 //0x53//0x3b
#define ACCEL_DATA_ADDR 0x32
#define ACCEL_PWRCTRL_ADDR 0x2d
#define ACCEL_MEASURE_MODE 0x08

signed int accel_data[6];
signed int x,y,z;
float x1,y1,z1,R1,R2,R3;
float teta=0, gama=0, fi=0;
float teta1=0, gama1=0 , fi1=0 ;
signed int16 AX,AY,AZ;

void main ()
{
// initialize

while(true) {
lcd_init();

// Tell the accelerometer to wake up
i2c_start();
i2c_write(ACCEL_WRITE_ADDR);
i2c_write(0X31);
i2c_write(0X0B);////+-16
//i2c_write(0X01);///+-4g
i2c_stop();

I2C_start();
i2c_write(ACCEL_WRITE_ADDR);
i2c_write(ACCEL_PWRCTRL_ADDR);
i2c_write(ACCEL_MEASURE_MODE);
I2C_stop();

// Read data from the accel
i2c_start();
i2c_write(ACCEL_WRITE_ADDR);
i2c_write(ACCEL_DATA_ADDR);
i2c_start();
i2c_write(ACCEL_READ_ADDR);
accel_data[0] = i2c_read(); //x0
accel_data[1] = i2c_read(); //x1
accel_data[2] = i2c_read(); //y0
accel_data[3] = i2c_read(); //y1
accel_data[4] = i2c_read(); //z0
accel_data[5] = i2c_read(0); // z1, NACK on last read
i2c_stop();
//////////concatenar datos adxl345/////////////
x=((accel_data[1])<<8)|accel_data[0] ;
y=((accel_data[3])<<8)|accel_data[2];
z=((accel_data[5])<<8)|accel_data[4];

//////////////////////////multiplicar 16*2/2^13//////////////////
x1=(float)x * 0.00390625;
y1=(float)y * 0.00390625;
z1=(float)z * 0.00390625;
////////////////////////////////////////////////
// R=sqrt((x1*x1)+(y1*y1)+(z1*z1));

R1=sqrt((y1*y1)+(z1*z1));
R2=sqrt((x1*x1)+(z1*z1));
R1=sqrt((y1*y1)+(x1*x1));

// teta=acos(x1/R);
teta=atan(x1/R1);
teta=teta * 57,296;

// gama=acos(y1/R);
gama=atan(y1/R2);
gama=gama*57,296;

// fi=acos(z1/R);
fi= atan(R3/z1);
fi=fi*57,296;

AX=(TETA*10)*0.1;
AY=(GAMA*10)*0.1;
AZ=(FI*10)*0.1;

printf(lcd_putc,"\f %d %d %d ",x,y,z);
printf(lcd_putc,"\n %LD %LD %LD ",AX,AY,AZ);
DELAY_MS(100);
}

}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Apr 10, 2013 11:27 pm     Reply with quote

When you have a program that has many steps for complicated math
operations, you can debug it by putting in printf statements after each
set of calculations. You can check if the intermediate values look correct.
If they don't look correct, then study the lines of code for those
calculations and fix the code.

This is a standard debugging technique: Display intermediate results.

Quote:

// Read data from the accel
i2c_start();
i2c_write(ACCEL_WRITE_ADDR);
i2c_write(ACCEL_DATA_ADDR);
i2c_start();
i2c_write(ACCEL_READ_ADDR);
accel_data[0] = i2c_read(); //x0
accel_data[1] = i2c_read(); //x1
accel_data[2] = i2c_read(); //y0
accel_data[3] = i2c_read(); //y1
accel_data[4] = i2c_read(); //z0
accel_data[5] = i2c_read(0); // z1, NACK on last read
i2c_stop();

// Display results of reading ADXL345.
printf("x1: %x x0: %x \n\r", accel_data[1], accel_data[0]);
printf("y1: %x y0: %x \n\r", accel_data[3], accel_data[2]);
printf("z1: %x z0: %x \n\r", accel_data[5], accel_data[4]);


//////////concatenar datos adxl345/////////////
x=((accel_data[1])<<8)|accel_data[0] ;
y=((accel_data[3])<<8)|accel_data[2];
z=((accel_data[5])<<8)|accel_data[4];

// Display results of combining 8-bit values into 16-bit.
printf("x = %x \n\r", x);
printf("y = %x \n\r", y);
printf("z = %x \n\r", z);


If the printf statements show incorrect intermediate results, then
look closely at your code and find the problems.
dorinm



Joined: 07 Jan 2006
Posts: 38

View user's profile Send private message

PostPosted: Sat Apr 13, 2013 3:37 pm     Reply with quote

I have ported the AVR AHRS code for sparkfun 9dof imu (https://www.sparkfun.com/products/10724) to 18f26K22 (clocked@64MHZ because I needed 50Hz output and calculations take ~3msec. plus other functionalities...). If it helps I can send it to you (it uses the magnetometer but it can be easily cut off). It is pretty straightforward (DCM algorithm it's easy to understand, just plain vector math).

Just for one's curiosity, the same code for Atmega @8MHz (8 (avr)MIPS) runs in about 4-4.8milliseconds (compared to 16(pic)MIPS ), so it seems that AVR is still faster...
...however!: with some code optimizations (inlines and fast trig.math algo's), the PIC can return the same result in ~1.5milliseconds, while the avr in ~3msec =)) , so it seems that the CCS compiler is really good at code generation and optimization, compared to "de facto" winAVR.
phuongnt



Joined: 25 Apr 2013
Posts: 3

View user's profile Send private message

PostPosted: Thu May 02, 2013 3:21 am     Reply with quote

dorinm wrote:
I have ported the AVR AHRS code for sparkfun 9dof imu (https://www.sparkfun.com/products/10724) to 18f26K22 (clocked@64MHZ because I needed 50Hz output and calculations take ~3msec. plus other functionalities...). If it helps I can send it to you (it uses the magnetometer but it can be easily cut off). It is pretty straightforward (DCM algorithm it's easy to understand, just plain vector math).

Just for one's curiosity, the same code for Atmega @8MHz (8 (avr)MIPS) runs in about 4-4.8milliseconds (compared to 16(pic)MIPS ), so it seems that AVR is still faster...
...however!: with some code optimizations (inlines and fast trig.math algo's), the PIC can return the same result in ~1.5milliseconds, while the avr in ~3msec =)) , so it seems that the CCS compiler is really good at code generation and optimization, compared to "de facto" winAVR.


Hi dorinm,

I'm interested in your ported AHRS code. Could you please share?

Thanks
DIEGODAVICINO



Joined: 24 Jun 2020
Posts: 1

View user's profile Send private message

PostPosted: Wed Jun 24, 2020 12:00 pm     Reply with quote

Hello. I am new to this, could someone explain why I divide by 256 ? The rest I understand everything.
Code:

x = make16((accel_data[1]), (accel_data[0]));
y = make16((accel_data[3]), (accel_data[2]));
z = make16((accel_data[5]), (accel_data[4]));
     
x1 = (float)x/256;
y1 = (float)y/256;
z1 = (float)z/256;
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Jun 24, 2020 7:06 pm     Reply with quote

We don't even know if his code works. There are some other posts on
this forum of ADXL345 drivers that are much more likely to work.
Examples:

http://www.ccsinfo.com/forum/viewtopic.php?t=52148
http://www.ccsinfo.com/forum/viewtopic.php?t=51309&start=1
Ttelmah



Joined: 11 Mar 2010
Posts: 19539

View user's profile Send private message

PostPosted: Wed Jun 24, 2020 11:49 pm     Reply with quote

For the question 'why /256', this is from the chip's datasheet.
The sensitivity is given as min 230, max 282, typical 256 counts/g.
So by dividing the counts by 256, you get a figure in g.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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