NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » 3-Axis Accelerometer Project with Hitachi H48C

April 23, 2011
by xodin
xodin's Avatar

Hey all,

First, I would like to say thanks to Mike and Humberto for starting up this site/business! What a huge contribution to the world of hobby electronics, you've certainly made microprocessor programming appear easy to me, when it had first seemed insurmountable.

Now, on to business. For any who are interested, I have created a simple program for interfacing the ATmega168 with an H48C 3-axis accelerometer. This particular accelerometer uses a bi-directional I/O pin for communication with a built-in MCP3204 A/D converter array. It allows you to select 1 of 4 channels (X-Axis, Y-Axis, Z-Axis, and Vref), and read the result. The X, Y, and Z axises can be converted to multiples of G (9.8m/s^2), using the formula provided in the spec sheet (Axis-Vref)*0.0022. I decided to use standard I/O pins for manual clock control and data communication, though I understand this is also compatible with an SPI serial interface.

This was my first non-nerdkits-guided project, so I apologize for any shortcomings, but I hope this helps those graduating to sensors that are beyond the basics.

If anyone has any questions, comments, or efficiency/technique recommendations, please let me know!

Xodin

Here's the datasheets:

http://www.parallax.com/dl/docs/prod/acc/HitachiH48C3AxisAccelerometer.pdf http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf

Here's the device:

http://www.mouser.com/ProductDetail/Parallax/28026/?qs=sGAEpiMZZMtzpSA5GSDwaxRrDnKfDfCF

Here's the code:

#include <stdio.h>

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

#define X_AXIS 0
#define Y_AXIS 1
#define Z_AXIS 2
#define V_REF  3

void sensor_setup() {
    DDRC |= (1<<PC5) | (1<<PC4) | (1<<PC3);
    PORTC |= (1<<PC5) | (1<<PC4) | (1<<PC3);
}

void toggle_clock() {
    PORTC &= ~(1<<PC5);
    delay_us(1);
    PORTC |= (1<<PC5);
    delay_us(1);
}

uint16_t get_sensor_data(uint8_t channel) {
    uint16_t value = 0;
    uint8_t c;

    // set PC3 (CS) to low initiates comms
    PORTC &= ~(1<<PC3);

    // ensure PC4 (DIO) is set to output high prior to starting
    DDRC |= (1<<PC4);
    PORTC |= (1<<PC4);

    // first clock with PC3 (CS) low and DIO high constitutes start bit
    toggle_clock();
    // second clock sets single-ended input mode
    toggle_clock(); 
    // third clock is for irrelavent bit applicable only to MCP3208
    toggle_clock();

    // fourth and fifth clocks choose channel, 0 = X Axis, 1 = Y Axis, 2 = Z Axis, 3 = Vref
    if ( channel == 0 ) {
        PORTC &= ~(1<<PC4);
        toggle_clock();
        toggle_clock();
    } else if ( channel == 1 ) {
        PORTC &= ~(1<<PC4);
        toggle_clock();
        PORTC |= (1<<PC4);
        toggle_clock();
    } else if ( channel == 2 ) {
        PORTC |= (1<<PC4);
        toggle_clock();
        PORTC &= ~(1<<PC4);
        toggle_clock();
    } else if ( channel == 3 ) {
        PORTC |= (1<<PC4);
        toggle_clock();
        toggle_clock();
    } else {
        lcd_home();
        lcd_write_string(PSTR("WHOOPS"));
    }

    // sixth clock takes sample, PC4 (DIO) irrelavent
    toggle_clock();

    // set PC4 (DIO) to input and activate pull-up resistor
    DDRC &= ~(1<<PC4);
    PORTC |= (1<<PC4);

    // seventh clock should be low null bit on PC4 (DIO)
    toggle_clock();

    if ( PINC & (1<<PC4) ) {
        lcd_home();
        lcd_write_string(PSTR("ERROR"));
    }

    //lcd_line_two();
    //lcd_write_string(PSTR("Out: "));

    // eighth through 19th clocks should be axis value MSB first on PC4 (DIO)
    for(c = 12; c > 0; c--) {
        toggle_clock();

        if ( PINC & (1<<PC4) ) {
            //lcd_write_string(PSTR("1"));
            value |= (1<<(c-1));
        } else {
            //lcd_write_string(PSTR("0"));
        }
    }

    // set PC3 (CS) to high to stop comms
    PORTC |= (1<<PC3);

    return value;
}

int main() {
  int x_g, y_g, z_g;
  uint16_t x, y, z, vref;

  // setup the sensor I/O
  sensor_setup();

  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_clear_and_home();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);

  while(1) {
    // get values from sensor
    x = get_sensor_data(X_AXIS);
    y = get_sensor_data(Y_AXIS);
    z = get_sensor_data(Z_AXIS);
    vref = get_sensor_data(V_REF);

    // precursor to conversion specified in H48C spec sheet
    x_g = x - vref;
    y_g = y - vref;
    z_g = z - vref;

    // output to LCD, 2nd values are G readings using conversion formula specified in H48C spec sheet
    lcd_home();
    fprintf_P(&lcd_stream, PSTR("X: %4d - %+2.2f     "), x, (double) (x_g * 0.0022));
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Y: %4d - %+2.2f     "), y, (double) (y_g * 0.0022));
    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR("Z: %4d - %+2.2f     "), z, (double) (z_g * 0.0022));
    lcd_line_four();
    fprintf_P(&lcd_stream, PSTR("Vref: %4d          "), vref);

    // output G values to computer
    fprintf_P(&uart_stream, PSTR("X: %2.2f\r\n"), (double) (x_g * 0.0022));
    fprintf_P(&uart_stream, PSTR("Y: %2.2f\r\n"), (double) (y_g * 0.0022));
    fprintf_P(&uart_stream, PSTR("Z: %2.2f\r\n"), (double) (z_g * 0.0022));
    delay_ms(50);
  }

  return 0;
}
April 24, 2011
by lnino
lnino's Avatar

Nice work. Thanks for sharing. I will give it a try when I finished my current project.

April 24, 2011
by Ralphxyz
Ralphxyz's Avatar

Hi Xodin, welcome and thank you for sharing your project. Are you going to do a gyro to go with it, hint hint?

Ralph

April 24, 2011
by xodin
xodin's Avatar

Thanks, that's a good idea. I will have to get one to try it out. Perhaps I should also get a magnetometer while I'm shopping.

Post a Reply

Please log in to post a reply.

Did you know that hobby servos are controlled through a particular kind of PWM (Pulse Width Modulation) signal? Learn more...