April 23, 2011
by xodin
|
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;
}
|