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 » Accelerometer project

January 21, 2010
by lcruz007
lcruz007's Avatar

Greetings!

I want to share with you guys a project I am developing with 2 microcontrollers. It is a remote controller with an accelerometer...

Let me know what you think about it :)

It is a memsic 2125 accelerometer, it is embedded on a remote controller I programed, which has an IR LED. The other microcontroller, reads the information sent by the LED and does something depending on the position of the controller.

You can see my project on this video I uploaded:

http://www.youtube.com/watch?v=9gti5_jBjJE

January 22, 2010
by Phrank916
Phrank916's Avatar

Very cool! Just the other day I was looking at some accelerometer spec sheets trying to brainstorm on what can be done with them. This is a cool project with lots of possible applications.

Ted

January 22, 2010
by lcruz007
lcruz007's Avatar

Thanks for your comment! :) It is indeed a really interesting project that has many other possible applications other than turning on LEDs as you said. Maybe controlling the servosquiter project with the remote controller and the accelerometer...? :p

I encourage you to try a accelerometer. :) You can try with a cheap one. Mine, is a memsic 2125 two-axis accelerometer. It is like $30 USD, and it perfectly fits the breadboard.

June 25, 2010
by Ralphxyz
Ralphxyz's Avatar

Could someone post some PWM code to read the accelerometer. Or some links to where you learned this.

Or even some discussion on how to read PWM would help.

I haven't the slightest idea where to start, what pins are used? How is the pulse timed?

I would assume a pin is pulled high by the PWM does one use a timer on the MCU or do you do use a timer in code?

Sure would appreciate the help.

Ralph

June 30, 2010
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Ralph,

Reading a PWM signal that is being generated by another chip is a great skill to learn. You basically need two things, a pin change interrupt to detect when a pin has changed state, and a timer to count how long the pin was high for. We go over pin change interrupts in our interrupts tutorial. You can connect the PWM input to any pin on your MCU that can be used to trigger a pin change interrupt (which is most of the regular digital pins). You also need to set up a timer to be running. Your pin change interrupt will look something like this

 ISR(pin_change_vector){

    if(my_pin_is_high){
      //reset the counter to 0
    }
    if(my_pin_is_low){
      //grab the current count on the counter
      //process the duty cycle or alert the main loop that a new value is ready
    }

 }

The interrupt hanlder just resets the count to 0 when the pin goes from low to high, then when the pin goes back to low you grab the current value of the timer and interpret that as the PWM duty cycle. You will have to understand the PWM that the accelerometer chip is outputting so you can know how to set up the timer, and how to interperet the count on the timer.

The implementation above will also not reject any random glitches on your PWM line that might cause false interrupts, however it is good enough to get started with. Once you need a more robust system you can add checks and redundancy on top of it.

Hope that is enough to get you started. Let us know how your project progresses!

Humberto

July 01, 2010
by Ralphxyz
Ralphxyz's Avatar

Thanks once again Humberto, you and Mike are such a help.

I had PWM on my list of things to learn eventually but because I grabbed a memsic 2125 accelerometer at Radio Shack as part of a compulsive buying binge I now have to learn PWM.

First I'll do something with LEDs lighting up but what I really really want to do is a balance bot:

http://www.youtube.com/watch?v=3hJyiDPR9Gw

I was thinking some counterweights moved by a servo instead of a gyro might work, but then again it would be great to learn about gyros.

How about a self righting balance bot or even one that puts out a arm to catch a fall and then recovers now that would be fun.

Ralph

February 14, 2011
by cujofever
cujofever's Avatar

I am trying to make a program to read an analog devices accelerometer (adxl-213). I think I am very close however my Ax goes negative and that should not be possible. At the end of my isr I zero out my timer variable as an attempt to keep my timer variable a small number. I measured the pwm on an oscilloscope and the high time ranges between 2milliseconds and 6 msec depending on the tilt of the accelerometer. Does anyone understand why I am getting a negative number? Thanks!

// pwm.c

#define F_CPU 14745600

#include <stdio.h>
#include <stdlib.h>

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

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

// PIN DEFINITIONS:
volatile int32_t the_time;

volatile int32_t t1;

volatile int32_t t2;

volatile int32_t Ax;

void realtimeclock_setup() {
  // setup Timer0:
  // CTC (Clear Timer on Compare Match mode)
  // TOP set by OCR0A register
  TCCR0A |= (1<<WGM01);
  // clocked from CLK/64
  // which is 14745600/64, or 230,400 increments per second
//see page 106 of atmega datasheet
  TCCR0B |= (1<<CS01) | (1<<CS00);
  // set TOP to 5
  // so 0 through 4 equals 5 events
  OCR0A = 4;
  // enable interrupt on compare event
  // (230,400 / 5 = 46,080 per second)
  TIMSK0 |= (1<<OCIE0A);
}
init_accelerometer(){

//make PC4 input pin
DDRC &= ~(1<<PC4);

//turn on pullup resistor
PORTC |= (1<<PC4); // turn on internal pull up resistor for PC5

//Enable PIN Change Interrupt 1 - This enables interrupts on pins //PCINT14...8 see p70 of datasheet
PCICR |= (1<<PCIE1);

//Set the mask on Pin change interrupt 1 so that only PCINT12 (PC4) triggers //the interrupt. see p71 of datasheet
PCMSK1 |= (1<<PCINT12); }

SIGNAL(SIG_OUTPUT_COMPARE0A) {
  // when Timer0 gets to its Output Compare value (4),
  // 20 micro seconds has elapsed (0.0000217 seconds).
  //bump the_time up by 1
  the_time++;
}

//Interrupt Routine that happens when pc4 is exposed
ISR(PCINT1_vect){
//if pin pc4 is 1 then set t1 = the_time
if(PINC & (1<<PC4)) {
t1 = the_time;
}
//when pin pc4 turns to 0 then set t2 = the_time  
else {
t2 = the_time;
}
Ax = t2 - t1;
the_time = 0;
}

int main() {

realtimeclock_setup();

t1 = 0;

t2 = 0;

Ax = 0;

init_accelerometer();

  // init lcd
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // init serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // turn on interrupt handler
  sei();

  while(1) {
lcd_home();
    fprintf_P(&lcd_stream, PSTR("%16.2f micro sec"), (double) Ax * 20);

}
  return 0;
}
February 15, 2011
by cujofever
cujofever's Avatar

I rewrote the isr. Now it makes more sense to me even though it doesn't work. t1 and t2 are counting up on the lcd but Ax stays zero. I have read every post I can find about reading pwm and I can't figure out what is wrong. Please help! Thanks

ISR(PCINT1_vect){
//if pin pc4 is 1 then set t1 = the_time

t1 = the_time;

while(PINC & (1<<PC4)) {
    // do nothing
  }
//when pin pc4 turns to 0 then set t2 = the_time

t2 = the_time;

Ax = (t2 - t1);

}
February 16, 2011
by mrobbins
(NerdKits Staff)

mrobbins's Avatar

Hi cujofever,

The reason you'll always get zero here is that this is running inside an interrupt handler ISR(PCINT1_vect). While it's handling PCINT1_vect, all other interrupts are blocked. This includes the timer interrupt that would otherwise properly increment the the_time variable!

I think you're going to have to re-think the way that you build your pin change interrupt handler. For example, have it simply check what the transition was -- if it was a low-high transition then set t1. Or if it was high-low, set t2. That way, the interrupt handler will return almost immediately so your timer can continue to increment.

Hope that helps!

Mike

February 16, 2011
by cujofever
cujofever's Avatar

Mike, thank you so much for your help!! I changed the isr to the following and it seems to be working.

//Interrupt Routine that happens when pc4 is exposed
ISR(PCINT1_vect){
//if pin pc4 is 1 then set t1 = the_time
if (PINC & (1<<PC4)) {
t1 = the_time;
  }
//when pin pc4 turns to 0 then set t2 = the_time  
else
{
t2 = the_time;
Ax = (t2 - t1);
}
}
February 19, 2011
by cujofever
cujofever's Avatar

I would now like to read two channels of pwm. I like Mike's idea of sensing rising or falling edges. It appears that the only interrupt pins that sense rising or falling edges are the int0 and int1 pins. Currently I'm using those pins for the lcd. Without the lcd I would have no chance of writing and debugging this program. It is my only window into what is going on. Are the int0 and int1 pins the only pins that allow sensing of rising and falling edges?

So lets assume that I am going to use pcint11 and pcint12 for my x and y channels. Is it possible to have on interrupt service routine for pcint12 and a different isr for pcint11? How do I link the pin to the isr?

That would be a really handy thing to be able to do. Then I wouldn't have so many if statements under the isr. I found a project on Avr freaks where the author is doing exactly what I'm trying to do. Unfortunately all the code is in assembly so it is difficult to understand. He does mention that he has four separate isr's the rising and falling edges of the two pwm channels.

With my current single axis program I tried setting a pullup resistor wondering if it would reduce noise and help with resolution. Seemed to make no difference. Should the pullup resistor be turned on or off?

February 20, 2011
by Ralphxyz
Ralphxyz's Avatar

cujofever, have you looked at the Wii Nunchuck that Rick and esoderberg did.

That code covers the accelerometer and gyro coming from the WII Nunchuck and motion plus add on.

Ralph

Post a Reply

Please log in to post a reply.

Did you know that two resistors can be used to make a voltage divider? Learn more...