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.
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
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP