Wednesday, March 30, 2011

Accelerometer calibration code

This is the microcontroller code that I used to calibrate the SCA3000 accelerometer as described in the previous post. This will send the measurements through the serial port at a baud rate of 9600.

/* Calibration routine for the SCA3000 accelerometer
 * using the WaveShare STK128+ Standard development
 * board with voltage level jumper set to 3.3 V

 * SCA3000 MOSI -> PB2(MOSI)
 * SCA3000 MISO -> PB3(MISO)
 * SCA3000 SCK -> PB1(SCK)
 * SCA3000 CSB -> PB4
 * SCA3000 RST -> PB5
 * SCA3000 INT (not connected)
 * SCA3000 VIN -> 5 V from USB port
 * SCA3000 GND -> common ground

 * CP2102 USB Converter RX -> PD3 (TXD1)
 * Onboard LED -> PB0
 */


#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>
#include <string.h>


// _delay_loop_2(18432) = 0.01 s

// Accelerometer readings (x, y, z)
int16_t readings[3];


void SPI_MasterInit(void)
{
   uint8_t i;

   for(i = 0; i < 100; i++)  // 1 sec delay
      _delay_loop_2(18432);

   // Enable pull-up on PB4(CS0)
   PORTB = _BV(PB4);
  

   // Set PB0(LED), PB1(SCK), PB2(MOSI) and PB5(RST0) as output low
   // Set PB4(CS0) as  output high
   DDRB = _BV(DDB0) | _BV(DDB1) | _BV(DDB2) | _BV(DDB4) | _BV(DDB5);

   // Enable SPI, Master, set clock rate fck/16
   SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0); // 460.8 kHz

   for(i = 0; i < 100; i++)  // 1 sec delay
      _delay_loop_2(18432);

   // Release SCA3000 reset (PB5)
   PORTB |= _BV(PB5);

   for(i = 0; i < 25; i++)  // 0.25 sec delay
      _delay_loop_2(18432);
}


// Read accelerometer x, y and z axis
void readAccelerometer(int16_t* reads)
{
   uint8_t azh, azl, ayh, ayl, axh, axl;

   // Select SCA3000
   PORTB &= ~(_BV(PB4));

  
   SPDR = 0x09 << 2;
   while(!(SPSR & _BV(SPIF)));

   SPDR = 0x00;
   while(!(SPSR & _BV(SPIF)));
   azh = SPDR;
   SPDR = 0x00;
   while(!(SPSR & _BV(SPIF)));
   azl = SPDR;
   SPDR = 0x00;
   while(!(SPSR & _BV(SPIF)));
   ayh = SPDR;
   SPDR = 0x00;
   while(!(SPSR & _BV(SPIF)));
   ayl = SPDR;
   SPDR = 0x00;
   while(!(SPSR & _BV(SPIF)));
   axh = SPDR;
   SPDR = 0x00;
   while(!(SPSR & _BV(SPIF)));
   axl = SPDR;


   // Deselect SCA3000
   PORTB |= _BV(PB4);


   reads[2] = ((((int16_t)azh) << 8) + azl) >> 3;
   reads[1] = ((((int16_t)ayh) << 8) + ayl) >> 3;
   reads[0] = ((((int16_t)axh) << 8) + axl) >> 3;
}


// Take 64 readings and calculate the average
void readAverage(void)
{
   int32_t x_accu = 0;
   int32_t y_accu = 0;
   int32_t z_accu = 0;
    
   for (uint8_t i = 0; i < 64; i++)
   {
       readAccelerometer(readings);

       x_accu += readings[0];
       y_accu += readings[1];
       z_accu += readings[2];

       _delay_loop_2(7089);    // 1/260 s delay
   }

   
readings[0] = x_accu / 64;
   readings[1] = y_accu / 64;
   readings[2] = z_accu / 64;
}


static int uart_putchar1(char c)
{
   loop_until_bit_is_set(UCSR1A, UDRE1);
   UDR1 = c;
   return 0;
}


int main(void)
{
   uint8_t i;

   char buffer[64];

   for(i = 0; i < 100; i++)  // 1 second delay
      _delay_loop_2(18432);

   /* enable serial port UART1 */ 
   /* Set baud rate : 9600 bps @ 7.3728 MHz */
   UBRR1L = (unsigned char)(47);
   /* Enable transmitter */
   UCSR1B = _BV(TXEN1);

   SPI_MasterInit();

   
   while(1)
   {
      PORTB ^= 0x01;  // toggle LED

      readAverage();

      sprintf(buffer, "%5i %5i %5i", readings[0], readings[1], readings[2]);

      for(i = 0; i < strlen(buffer); i++)
         uart_putchar1((unsigned char)(buffer[i]));

      uart_putchar1('\r');
      uart_putchar1('\n');
   }
}


UPDATE :
I am now using an improved procedure for accelerometer calibration, which adds a correction for axis misalignments (cross-talk).
See:

 

2 comments:

  1. interesting blog. It would be great if you can provide more details about it. Thank you

    ReplyDelete