TransWikia.com

How can I set a timer?

Arduino Asked by Damon Swart on July 24, 2020

I would like to set a stopwatch timer that will determine how long an input is in a certain state before changing. I want to set it so that, depending on the output my code executes one of 2 switch cases. But my trouble comes in on setting a timer. Is there a function I could use? Or a method that somebody knows? The time that the input will be for each case is not fixed so I cannot use a delay.

2 Answers

Your title is about “setting a timer”, but your actual question is about measuring the length of a pulse. There are two functions provided by the Arduino IDE for this purpose, pulseIn() and pulseInLong():

  • pulseIn() is based on a carefully timed delay loop. It has a resolution of the order of one microsecond, but will not count the time spent servicing interrupt requests. It works best for very short pulses timed with interrupts turned off.
  • pulseInLong() is based on micros(). It has a resolution of 4 µs and will not work properly if interrupts are turned off. It works best for longer pulses where its limited resolution and interrupt latency are tolerable.

Both of these are blocking functions: they completely block your sketch while they perform the measurement. If you don't want your sketch to be unresponsive during this time, you can write a non-blocking version of pulseInLong() using a finite-state machine like this:

// Measure the length of a pulse in a non-blocking manner.
// Returns 0 if no measurement is available at the time of the call.
void get_pulse_length() {
    static enum {
        INITIAL_WAIT,    // wait for the first (partial) pulse to end
        BETWEEN_PULSES,  // wait for the pulse to start
        WITHIN_PULSE     // wait for the pulse to end
    } pulse_state = INITIAL_WAIT;
    static uint32_t pulse_start;  // when the current pulse started

    uint8_t pin_state = digitalRead(pulse_pin);
    uint32_t now = micros();
    switch (pulse_state) {
        case INITIAL_WAIT:
            if (pin_state == LOW)
                pulse_state = BETWEEN_PULSES;
            break;
        case BETWEEN_PULSES:
            if (pin_state == HIGH) {
                pulse_start = now;
                pulse_state = WITHIN_PULSE;
            }
            break;
        case WITHIN_PULSE:
            if (pin_state == LOW) {
                pulse_state = BETWEEN_PULSES;
                return now - pulse_start;
            }
            break;
    }
    return 0;
}

Note that this measures high pulses. You will have to swap HIGH and LOW if you want to measure low pulses. You would use it like this:

void loop() {
    uint32_t pulse_length = get_pulse_length();
    if (pulse_length) {
        // handle the pulse
    }
}

The resolution of the measurement is the execution time of loop(), so you have to make sure there is nothing blocking there, and specially no calls to delay(). If you need a better resolution from a non-blocking method, you can use interrupts to trigger the measuring process:

volatile uint32_t pulse_start, pulse_length;
volatile bool pulse_valid;

void on_rise() {
    pulse_start = micros();
    attachInterrupt(digitalPinToInterrupt(pin), on_fall, FALLING);
}

void on_fall() {
    pulse_length = micros() - pulse_start;
    pulse_valid = true;
    attachInterrupt(digitalPinToInterrupt(pin), on_rise, RISING);
}

uint32_t get_pulse_length()
{
    if (!pulse_valid) return 0;
    noInterrupts();
    uint32_t pulse_length_copy = pulse_length;
    pulse_valid = false;
    interrupts();
    return pulse_length_copy;
}

void setup() {
    attachInterrupt(digitalPinToInterrupt(pin), on_rise, RISING);
}

This should give you the resolution of micros(), i.e. 4 µs, but occasionally you may get results that are slightly off if interrupts happen to be disabled when the input transitions. If this is unacceptable, the only other options I see is to use a hardware timer with input capture capability. You will have to look at the datasheet of your microcontroller to see how it works, and maybe do a Web search for “Arduino input capture”.

Answered by Edgar Bonet on July 24, 2020

Even though you are not running an actual (complex) Operating System, you should adhere to common practices. For an Arduino, you should, in many cases, avoid directly controlling the hardware so as to be compatible with as many existing libraries for your particular Arduino platform as possible.

Setting the timer directly (if you are using an official Arduino Uno which contains an Atmel328P processor the processor's timers are covered in section 14 of the Atmel328P Specifications) may cause unexpected results should you use a library which expects the timer to run without being altered.

Instead, consider using the millis() function built into the Arduino IDE. The function returns the current number of milliseconds since the Arduino was powered up. Record this value in your code. Then, if you want to know if One Second has elapsed, get the new value of millis and subtract this saved value from it and see if it is greater than 1000. When that is true, One Second has elapsed.

Answered by st2000 on July 24, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP