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 » Get time between sensor readings

August 18, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi all, Hope everyone is having a good weekend.

I have been pondering something for quite some time, and that is - How do you go about getting the actual time between sensor readings? Or is it possible to write a GetTime() function? Where this function would have an init() macro that started the counter going, then I could call GetTime() and it would output the elapsed time in miliseconds.

Any thoughts on this topic is greatly appreciated!

Thanks,

Dave

August 18, 2013
by esoderberg
esoderberg's Avatar

Dave,

Here are a few code snippets that I'm using to time the delta between inputs from an ABS speed sensor.

Set up an interupt on PB0:

//Make PB0 input pin
DDRB &= ~(1<<PB0);

PORTB |= (1<<PB0);

PCICR |= (1<<PCIE0);

PCMSK0 |= (1<<PCINT0);//only trigger on PB0

This is the code that will execute on every change in PB0:

ISR(PCINT0_vect){//     V1
//Trigger by hall switch for V1
tt[1]=t-ttlast[1];//delta time between Speed Sensor hits in .1 msec units
ttlast[1] = t;

}

My code has a clock recording time (in .1 msec increments) in variable t.

time setup:

void time_setup() {
// setup Timer0:
// CTC (Clear Timer on Compare Match mode)
// TOP set by OCR0A register
TCCR0A |= (1<<WGM01);
// clocked from CLK/64, which is 16000000/64, or 250000 increments per second---
//with 16Mhz crystal
TCCR0B = 0;
TCCR0B |= (1<<CS01) | (1<<CS00);
// set TOP to 24
// because it counts 0, 1, 2,..23,24, 0, 1, 2 ...
// so 0 through 24 equals 25 events
OCR0A = 31;//for 20Mhz crystal  use 24 with 16MHz

// enable interrupt on compare event
// (250000 / 25 = 10000 per second) with ocr0a = 24  
TIMSK0 |= (1<<OCIE0A);
}

// t will store the elapsed time // (10000 = 1 second)

////////////////////////////////////
ISR(TIMER0_COMPA_vect){ // when Timer0 gets to its Output Compare value
t++;
}
August 18, 2013
by JimFrederickson
JimFrederickson's Avatar

Hello Dave,

The quick answer is, of course it can be done.

There were, are?, examples in the Library to illustrate this. However; the Library is
still not accessible...

Time, and understanding it's use, is an essential function of ANY real world
application. (Which I assume you realize or are realizing...)

There are very few Projects that I have where I do not have some sort of "Time Base".

Pretty much all of the Basic NerdKit examples really either ignore time, or lead you in
a little bit of the wrong direction.

Like many things, to make some things easier other things have to become harder.

One thing you need to really do is start looking at your Program, your Program Goals, in
a light a little different than just a series of steps...

Let's look at on of the Nerdkit Examples of the "LED Blink"...

            int main() {
      // LED as output
      DDRC |= (1<<PC4);

            // loop keeps looking forever
      while(1) {
        // turn on LED
        PORTC |= (1<<PC4);

            //delay for 500 milliseconds to let the light stay on
        delay_ms(500);

        // turn off LED
        PORTC &= ~(1<<PC4);

              //delay for 500 milliseconds to let the light stay off
        delay_ms(500);

      }

      return 0;
    }

This is more of a "Step Oriented Approach"...

While this approach works for SIMPLE situations, often times a little more complexity is
required.

I would recommend you adopt an approach of "Conditions, Events, Actions"...

A key thing to understand is that the AVR MCU is ALWAYS doing something. Whether you
need it to do something or not it is ticking away. (Well there are sleep modes, that
kind throw this out of whack, but that is not in use here.)

The Program you write for the AVR MCU, which is always doing something, can ONLY DO 1
thing at a time. (Now there are some elements of the AVR MCU the operate independently
and it is able to do some things simultaneous Your Program only does 1 thing at a
time.)

In order for you to "seemingly do multiple things at once" you need to schedule those
as a series of "Single Tasks" that are executed so that they do not interfere with each
other.

While the "Delay_ms() Function" does create a delay of the specified amount of time, but it
also creates as "Stutter" in your Program.

So while the AVR MCU is "busily executing code to create the delay" your Program is
not advancing. (It is not checking conditions, looking at events, or performing actions...)

Let's stay with the "LED Blink example" for illustration purposes.

Let's say we wanted to add second Blinking LED with an Interval of 1 second.

Using the original "Step Process" you would come to:

    int main() {
      // LED as output
      DDRC |= (1<<PC3);
      DDRC |= (1<<PC4);

      // loop keeps looking forever
      while(1) {
        // turn on LED
        PORTC |= (1<<PC3);

        //delay for 500 milliseconds to let the light stay on
        delay_ms(1000);

        // turn off LED
        PORTC &= ~(1<<PC3);

        //delay for 500 milliseconds to let the light stay off

        delay_ms(1000);

        // turn on LED
        PORTC |= (1<<PC4);

        //delay for 500 milliseconds to let the light stay on

        delay_ms(500);

        // turn off LED
        PORTC &= ~(1<<PC4);

        //delay for 500 milliseconds to let the light stay off

        delay_ms(500);

      }

      return 0;
    }

There now you are blinking 2 Blinking LED's.
One with a 1 second interval, and the other with 1/2 second interval...

Not really...

What is happening is one Blinking LED does it's cycle, then the other Blinking LED does
it's cycle, and then each cycle repeats in the same order.

So there is a chain of Steps, but each one is deterministic on the other.

What you really want is for each one to be "Time Deterministic"...

A very simple, albeit not exact, way of making something "Time Deterministic" and I
think getting your thought process in a better direction is to implement something
like:

    //  Loop keeps looking forever

    while(1) {

    //  Do stuff here based on at a specific ms time

        delay_ms(1);

        mytime_ms++;

        if (mytime_ms > 999) {
            mytime_ms = 0;
            }
        }

So the example becomes:

    int main() {

    uint16_t    mytime_ms;

    uint8_t     LED1_state;
    uint8_t     LED2_state;

    //  Clear or ms, millisecond, timer

    //  LED as output  
    DDRC |= (1<<PC3);
    DDRC |= (1<<PC4);

    LED1_state = 0;
    LED2_state = 0;

    //  Loop keeps looking forever  
    while(1) {

        if (mytime_ms == 0) {
    //  Process for LED1
            if (LED1_state == 0) {
                LED1_state = 1;
    //  Turn on LED
                PORTC |= (1<<PC3);
                }
            else {
                LED1_state = 0;
    //  Turn off LED
                PORTC &= ~(1<<PC3);
                }
            }

    //  Process for LED2
        if (mytime_ms == 0 || mytime_ms == 499) {
            if (LED2_state == 0) {
                LED2_state = 1;
    //  Turn on LED
                PORTC |= (1<<PC4);
                }
            else {
                LED2_state = 0;
    //  Turn off LED
                PORTC &= ~(1<<PC4);
                }
            }
        //
        delay_ms(1);

        mytime_ms++;

        if (mytime_ms > 999) {
            mytime_ms = 0;
            }
        }
      return 0;
    }

That is a rudimentary method of having some form of a "Time Base".

It also encourages you to think more in terms of "Conditions, Events, Actions"...

I would really change it to, at the very least, this:

    void Task_LED1 (uint8_t LED_state) {
    //  Process for LED1
            if (LED_state == 0) {
    //  Turn on LED
                PORTC |= (1<<PC3);
                }
            else {
    //  Turn off LED
                PORTC &= ~(1<<PC3);
            }
        }

    void Task_LED2 (uint8_t LED_state) {
    //  Process for LED1
            if (LED_state == 0) {
    //  Turn on LED
                PORTC |= (1<<PC4);
                }
            else {
    //  Turn off LED
                PORTC &= ~(1<<PC4);
            }
        }

    int main() {

    uint16_t    mytime_ms;

    uint8_t     LED1_state;
    uint8_t     LED2_state;

    //  Clear or ms, millisecond, timer

    //  LED as output
    DDRC |= (1<<PC3);
    DDRC |= (1<<PC4);

    LED1_state = 0;
    LED2_state = 0;

    //  Loop keeps looking forever
    while(1) {

    //  Process for LED1
        if (mytime_ms == 0) {
            Task_LED1(LED1_state);
            if (LED1_state == 0) {
                LED1_state = 1;
                }
            else {
                LED1_state = 0;
                }
            }

    //  Process for LED2
        if (mytime_ms == 0 || mytime_ms == 499) {
            Task_LED2(LED2_state);
            if (LED2_state == 0) {
                LED2_state = 1;
                }
            else {
                LED2_state = 0;
                }
            }
        //
        delay_ms(1);

        mytime_ms++;

        if (mytime_ms > 999) {
            mytime_ms = 0;
            }
        }
      return 0;
    }

So now instead of the "Main Function" being just a series of steps it becomes more like
a scheduler.

If you have your LCD Hooked up and you adapt this program to count the tick ms's to
seconds, minutes, and hours you can see how far off of an "Accurate Time Base" this is.
(If course if you do that MAKE SURE to create a task to only update the display every 1
second and NOT continuously...)

In order to have something closer to an "Accurate Time Base" you have to use
interrupts.

This is ONLY to give you ideas.

Things to think about...

        //  Normally, I run my Tasks in "intervals" so I have "Count Down Timers" that are used to schedule each Task.  
        //  The above example become cumbersome if you wanted to run a task every 334ms?
        //  "Count Down Timers" solve that.

        //  If you have values that a Task will use they need to be defined globablly, or with Static.

        //  When, if, you start using interrupts keep every interrupt routine as SMALL AS POSSIBLE.
        //  Remember everything an Interrupt Task does has to fit within a Window so that it won't conflict with any other Interrupt Task.

        //  Remember Interrupt Routines create a concept of Foreground and Background so have to be careful/thoughtful when changing/using values 
        //  So that values being used by the Foreground Part of the Program are not being alter by the Background/Interrupt Routine part Part of the
        //  Program.

NOTE: While I do know that this does compile I do not have the means to "Test it" right
now. I expect that is does work properly. Currently I am, pretty literally, outside of
civilization and my resources are limited.

Hopefully, it just works, but the concepts are mostly sound.

They can be GREATLY expanded upon but just some things to think about...

August 19, 2013
by Ralphxyz
Ralphxyz's Avatar

Thanks Eric and Jim, man that is what I love about this forum the detail of the help has always been so great.

Ralph

August 20, 2013
by dvdsnyd
dvdsnyd's Avatar

Eric and Jim,

Thanks so much for your replies! I really appreciate the level of detail as well! I am sure I will have some questions once I start getting going on here.

Thanks again!

Dave

Post a Reply

Please log in to post a reply.

Did you know that a motor is harder to turn when its terminals are shorted together? Learn more...