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

PIC and ADXL345 SPI Accelerometer

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



Joined: 24 Jun 2004
Posts: 1908

View user's profile Send private message

PostPosted: Thu Oct 10, 2013 4:24 pm     Reply with quote

Code:
#include <18F4680.h>
#device adc=10
#FUSES WDT
#FUSES WDT128                   //Watch Dog Timer uses 1:128 Postscale
#FUSES INTRC_IO                 //Internal RC oscillator, no clock out
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES BORV28                   //Brownout reset at 2.8V
#FUSES PUT                      //Power Up Timer
#FUSES NOCPD                    //No EE protection
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOWRTD                   //Data EEPROM not write protected
#FUSES NOIESO                   //Internal External Switch Over mode disabled
#FUSES NOFCMEN                  //Fail-safe clock monitor disabled
#FUSES NOPBADEN                 //PORTB pins are configured as digital I/O on RESET
#FUSES BBSIZ4K                  //4K words Boot Block size
#FUSES NOWRTC                   //configuration not registers write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOCPB                    //No Boot Block code protection
#FUSES NOLPT1OSC                //Timer1 configured for higher power operation
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)

#use delay(clock=8000000,RESTART_WDT)
#use spi(MASTER, DI=PIN_C4, DO=PIN_C5, CLK=PIN_C3)

#include <lcd_driver.c>
#include <math.h>

// Jan 4 2011
// this is a completely tested and working example/driver to read the
// x,y,z accelerations from an ADXL345 accelerometer and convert
// to angle
// both PIC and ADXL run @3.3V (LCD too) so no level converters are required
// PIC runs @8MHz from internal oscillator

#use fast_io (B)
#use fast_io (C)

int1 refresh_screen = FALSE;
#define CS  PIN_C0
#define SPI_MODE_0   (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_1   (SPI_L_TO_H)
#define SPI_MODE_2   (SPI_H_TO_L)
#define SPI_MODE_3   (SPI_H_TO_L | SPI_XMIT_L_TO_H)

unsigned int16 x_accel, y_accel, z_accel;
float x_g, y_g, z_g;
float theta;

#include <Accelerometer Driver.c>
#include <Trig Functions.c>

#int_EXT1
void EXT1_isr(void) {
   refresh_screen = TRUE;
}

void main() {
   setup_oscillator(OSC_8MHZ|OSC_INTRC|OSC_31250|OSC_PLL_OFF); // setup internal oscillator to run @ 8MHz
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_CLOCK_DIV_2|ADC_TAD_MUL_0);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_MASTER | SPI_MODE_3 | SPI_CLK_DIV_4);
   setup_wdt(WDT_ON);
   setup_timer_0(RTCC_OFF);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
   setup_ccp1(CCP_OFF);
   setup_comparator(NC_NC_NC_NC);
   //enable_interrupts(INT_RTCC);
   enable_interrupts(GLOBAL);
   
   // LCD connections as per lcd_driver.c (same as that included in the CCS drivers)
   // The LCD is connected as shown on the Lab-X1 schematic:
    // PIC pin D0 - LCD D0
    // PIC pin D1 - LCD D1
    // PIC pin D2 - LCD D2
    // PIC pin D3 - LCD D3
    // PIC pin D4 - LCD D4
    // PIC pin D5 - LCD D5
    // PIC pin D6 - LCD D6
    // PIC pin D7 - LCD D7
    // PIC pin E0 - LCD RS
    // PIC pin E1 - LCD E
    // PIC pin E2 - LCD R/W
   
   // port B
   // B0 - unused - OUTPUT
   // B1 - INT1 - input
   // B2 - INT2 - input
   // B3 - unused - OUTPUT
   // B4 - unused - OUTPUT
   // B5 - unused - OUTPUT
   // B6 - unused - OUTPUT
   // B7 - unused - OUTPUT
   //port_b_pullups(TRUE);
   set_tris_b(0x06);
   output_b(0x00);
   
   clear_interrupt(INT_EXT1);
   ext_int_edge(1, L_TO_H);
   enable_interrupts(INT_EXT1);
   
   // port C
   // C0 - /CS - OUTPUT
   // C1 - unused - OUTPUT
   // C2 - unused - OUTPUT
   // C3 - SCLK - OUTPUT
   // C4 - MISO - INPUT
   // C5 - MOSI - OUTPUT
   // C6 - unused - OUTPUT
   // C7 - unused - OUTPUT
   output_c(0xff);
   set_tris_c(0x10);
   setup_accelerometer();
   
   set_tris_e(0x00);
   delay_ms(100);
   lcd_init();
   delay_ms(100);
   lcd_putc("\fAccelerometer Test");
   delay_ms(1000);

   while (TRUE) {
      restart_wdt();
      if (refresh_screen == TRUE) {
         refresh_screen = FALSE;
         read_accel();
         calc_angle(Y_PARALLEL_TO_PIPE);
         printf(lcd_putc,"\fAngle=%f", theta);
         lcd_putc(0xdf);
      }
   }
}


Code:
Accelerometer Driver.c:
#define WRITE              0x00
#define READ               0x80
#define MULTIBYTE_TRANSFER 0x40
#define BW_RATE_REGISTER   0x2c
#define NO_POWER_SAVING    0x00
#define HZ6_25             0x06
#define DATA_FORMAT_REGISTER  0x31
#define DATA_FORMAT_VALUE  0x0b
#define FIFO_CONTROL_REGISTER 0x38
#define FIFO_CONTROL_VALUE 0x00
#define DATA_REG           0x32
#define POWER_CONTROL_REGISTER   0x2d
#define POWER_CONTROL_VALUE   0x08
#define INT_ENABLE_REGISTER   0x2e
#define INT_ENABLE_VALUE   0x80
#define INT_MAP_REGISTER   0x2f
#define INT_MAP_VALUE      0x7f

#define SIGN_BIT           9
#define ACCEL_MASK         0x01ff

int8 read_accel_register(int8 reg) {
   int8 register_value;
   output_low(CS);
   delay_us(10);
   spi_write(READ | reg);
   register_value = spi_read(0x00);
   delay_us(10);
   output_high(CS);
   return (register_value);
}

void setup_accelerometer(void) {
   // set interrupt enable register
   output_low(CS);
   delay_us(1);
   spi_write(WRITE | INT_ENABLE_REGISTER);
   spi_write(INT_ENABLE_VALUE);
   delay_us(1);
   output_high(CS);
   
   // set interrupt map register
   output_low(CS);
   delay_us(1);
   spi_write(WRITE | INT_MAP_REGISTER);
   spi_write(INT_MAP_VALUE);
   delay_us(1);
   output_high(CS);
   
   // set data format register appropriately
   output_low(CS);
   delay_us(1);
   spi_write(WRITE | DATA_FORMAT_REGISTER);
   spi_write(DATA_FORMAT_VALUE);
   delay_us(1);
   output_high(CS);
   
   // set rate register to 6.25Hz conversion, no power saving mode
   output_low(CS);
   delay_us(1);
   spi_write(WRITE | BW_RATE_REGISTER);
   spi_write(NO_POWER_SAVING | HZ6_25);
   delay_us(1);
   output_high(CS);
   
   // set FIFO control register
   output_low(CS);
   delay_us(1);
   spi_write(WRITE | FIFO_CONTROL_REGISTER);
   spi_write(FIFO_CONTROL_VALUE);
   delay_us(1);
   output_high(CS);
   
   // set power control register appropriately
   output_low(CS);
   delay_us(1);
   spi_write(WRITE | POWER_CONTROL_REGISTER);
   spi_write(POWER_CONTROL_VALUE);
   delay_us(1);
   output_high(CS);
}

void read_accel(void) {
   int32 scratch;
   float scale;
   int1 neg_x = FALSE;
   int1 neg_y = FALSE;
   int1 neg_z = FALSE;
   x_accel = 0;
   y_accel = 0;
   z_accel = 0;
   output_low(CS);
   delay_us(1);
   spi_write(READ | MULTIBYTE_TRANSFER | DATA_REG);
   x_accel = spi_read(0); // x low byte
   x_accel = x_accel | ((int32)spi_read(0) << 8); // x high byte
   y_accel = spi_read(0); // y low byte
   y_accel = y_accel | ((int32)spi_read(0) << 8); // y high byte
   z_accel = spi_read(0); // z low byte
   z_accel = z_accel | ((int32)spi_read(0) << 8); // z high byte
   delay_us(1);
   output_high(CS);
   
   if (bit_test(x_accel, SIGN_BIT)) {
      neg_x = TRUE;
      x_accel = ~x_accel;
      x_accel++;
      x_accel = x_accel & ACCEL_MASK;
   }
   else {
      neg_x = FALSE;
   }
   
   if (bit_test(y_accel, SIGN_BIT)) {
      neg_y = TRUE;
      y_accel = ~y_accel;
      y_accel++;
      y_accel = y_accel & ACCEL_MASK;
   }
   else {
      neg_y = FALSE;
   }
   
   if (bit_test(z_accel, SIGN_BIT)) {
      neg_z = TRUE;
      z_accel = ~z_accel;
      z_accel++;
      z_accel = z_accel & ACCEL_MASK;
   }
   else {
      neg_z = FALSE;
   }
   
   // had problems when prototyping this as the 2 methods for calculating angle didn't agree due to
   // gain imbalances in the x, y and z accelerations
   // workaround is to note that all accelerations must add up to 1 g, so normalize
   // each by the the total magnitude to compensate
   
   scratch = ((int32)x_accel * (int32)x_accel) + ((int32)y_accel * (int32)y_accel) + ((int32)z_accel * (int32)z_accel);
   scale = sqrt((float)scratch);
   
   x_g = x_accel / scale;
   if (neg_x == TRUE) {
      x_g = -1 * x_g;
   }
   
   y_g = y_accel / scale;
   if (neg_y == TRUE) {
      y_g = -1 * y_g;
   }
   
   z_g = z_accel / scale;
   if (neg_z == TRUE) {
      z_g = -1 * z_g;
   }
}


Trig Functions.c:
Code:
#define X_PARALLEL_TO_PIPE 0
#define Y_PARALLEL_TO_PIPE 1
#define Z_PARALLEL_TO_PIPE 2

void calc_angle(int8 which) {
   float denominator, theta1, theta2;
   int1 add_180_to_theta1 = FALSE;
   int1 add_360_to_theta2 = FALSE;
   int1 rotate_90 = FALSE;
   
   // if x axis is parallel to pipe
   // theta (based on y) = sin^-1(-y/cos(sin^-1(x)))
   // theta (based on z) = cos^-1(z/cos(sin^-1(x)))
   
   // if y axis is parallel to pipe
   // theta (based on z) = sin^-1(-z/cos(sin^-1(y)))
   // theta (based on x) = cos^-1(x/cos(sin^-1(y)))
   
   // if z axis is parallel to pipe
   // theta (based on x) = sin^-1(-x/cos(sin^-1(z)))
   // theta (based on y) = cos^-1(y/cos(sin^-1(z)))
   
   // y parallel to pipe seems to be the most accurate
   
   switch (which) {
      case X_PARALLEL_TO_PIPE:
         denominator = cos(asin(x_g));
         theta1 = asin((-1 * y_g)/denominator);
         theta2 = acos(z_g/denominator);
         if (z_g < 0) {
            add_180_to_theta1 = TRUE;
         }
         if (y_g >= 0) {
            add_360_to_theta2 = TRUE;
         }
         break;
      case Y_PARALLEL_TO_PIPE:
         denominator = cos(asin(y_g));
         theta1 = asin((-1 * z_g)/denominator);
         theta2 = acos(x_g/denominator);
         if (x_g < 0) {
            add_180_to_theta1 = TRUE;
         }
         if (z_g >= 0) {
            add_360_to_theta2 = TRUE;
         }
         rotate_90 = TRUE;
         break;
      case Z_PARALLEL_TO_PIPE:
         denominator = cos(asin(z_g));
         theta1 = asin((-1 * x_g)/denominator);
         theta2 = acos(y_g/denominator);
         if (y_g < 0) {
            add_180_to_theta1 = TRUE;
         }
         if (x_g >= 0) {
            add_360_to_theta2 = TRUE;
         }
         rotate_90 = TRUE;
         break;
   }
   
   theta1 = theta1 * (180 / (4 * atan(1.0)));
   if (add_180_to_theta1 == TRUE) {
      theta1 = (-1 * theta1) + 180;
   }
   if (theta1 < 0) {
      theta1 = theta1 + 360;
   }
   
   theta2 = theta2 * (180 / (4 * atan(1.0)));
   if (add_360_to_theta2 == TRUE) {
      theta2 = (-1 * theta2) + 360;
   }
   
   if ((theta1 > 270) && (theta2 < 90)) {
      theta2 = theta2 + 360;
   }
   else if ((theta2 > 270) && (theta1 < 90)) {
      theta1 = theta1 + 360;
   }
   
   theta = (theta1 + theta2) / 2;
   if (rotate_90 == TRUE) {
      theta = theta + 90;
   }
   if (theta > 360) {
      theta = theta - 360;
   }
}
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