A sine wave is being fed to A0
and I would like to calculate the frequency. If a certain threshold is crossed, start the timer.....count 5 such crossings.....then stop the timer. The code is attached. Even though I understand while()
with logical operators such as while(State<5)
(shown in code), I do not understand the use of while(analogRead(A0))
. I would like to know how it functions in relation the code? The program runs fine as it is. Tested this in Tinkercad, and got erroneous readings when while(analogRead(A0))
was commented. Thank you so much for reading my question ^_^.
void loop()
{
while(State<5)
{
val=analogRead(A0);
if(val>200)
{
Serial.println("Threshold Crossed");
if(State==0)
{
Start_Time=millis();
}
State++;
while(analogRead(A0)); //I DON'T UNDERSTAND THIS
}
}
End_Time=millis();
Period=(Start_Time-End_Time)/5;
Frequency=1000/(Start_Time-End_Time)
Serial.println(Frequency);
State=0;
}
2 Answers 2
As you correctly note, while
typically takes a bool as argument. However, mainly for historical reasons, C/C++ don't really differentiate between int and bool, and therefore conditions can be of integer type. Any condition, be it in a while
or an if
, can be of int type. The condition evaluates to true if it is not zero. So the condition while(analogRead(A0));
loops (doing nothing) until analogRead
returns 0.
-
I think I get this now, this line just works as some sort of delay. Without while(analogRead(A0)), just after the first threshold is crossed (say val is now 202), state becomes +1 and the program loops to "val=analogRead(A0)" again. The val is now 210, so state will get incremented again.........and until state>5, val probably reaches upto 250. This will certainly give the wrong time period. On the other hand while(analogRead(A0)) keeps looping until false (0), gives enough time for val to cross threshold and then become less than threshold, so that the crossing can be detected again. Thanks!Nawaz– Nawaz2021年08月06日 07:20:15 +00:00Commented Aug 6, 2021 at 7:20
-
@Nawaz Not a delay, rather a "keep checking the value until it’s what I’m looking for". It has nothing to do with time, but value.Dave Newton– Dave Newton2021年08月06日 13:03:58 +00:00Commented Aug 6, 2021 at 13:03
-
Yes, but analogRead() may never return identical 0 (an offset of 20 mV would prevent it). Or only occasionally due to random noise. This is the equivalent to comparing floats with fixed values. Perhaps address that in your answer?Peter Mortensen– Peter Mortensen2021年08月06日 20:58:44 +00:00Commented Aug 6, 2021 at 20:58
What you have implemented, perhaps unknowingly, is called hysteresis. Your program spends most of its time in either of these two states:
- It is continually running the outer
while
loop, and the value read is ≤ 200. It gets out of this state as soon as it reads a value larger than 200. - It is continually running the inner
while
loop, and the value read is > 0. It gets out of this state only whenanalogRead()
returns zero.
Hysteresis is a good way to reject noise when you want to detect a
signal crossing a threshold. I would, however, recommend you use it in a
more controlled manner. Specifically, the second threshold should
probably be greater than zero, otherwise it won't be crossed unless the
signal goes all the way down to ground level. At a minimum, you can
replace the inner while
loop with something like:
while (analogRead(A0) >= 100)
/* wait until the signal gets low enough */;
For the record, this logic can be implemented in a non-blocking
fashion, which would let the program do other things, like responding to
button presses, while waiting for the transitions. For this, you need a
variable to record the current state of the signal (say, LOW
or
HIGH
) and record the state changes when the corresponding thresholds
are crossed.
The sketch below implements this non-blocking technique in loop()
,
whereas setup()
does block while waiting for the first LOW
→ HIGH
transition:
const int LOW_THRESHOLD = 100;
const int HIGH_THRESHOLD = 200;
uint8_t state; // either LOW or HIGH
uint8_t count = 0; // count the LOW -> HIGH transitions
uint32_t start_time;
void setup() {
// Wait for the first LOW -> HIGH transition.
while (analogRead(A0) >= LOW_THRESHOLD)
/* Wait for the LOW state. */;
while (analogRead(A0) < HIGH_THRESHOLD)
/* Wait for the HIGH state. */;
state = HIGH;
// Record transition time.
start_time = millis();
}
void loop() {
// Record transitions.
uint8_t val = analogRead(A0);
if (state == HIGH && val < LOW_THRESHOLD) {
state = LOW;
} else if (state == LOW && val >= HIGH_THRESHOLD) {
state = HIGH;
++count;
}
if (count >= 5) {
// Report measured frequency.
uint32_t end_time = millis();
int frequency = 1000 * count / (end_time - start_time);
Serial.println(frequency);
// Prepare for the next round.
start_time = end_time;
count = 0;
}
}
-
Re "Specifically, the second threshold should probably be greater than zero, otherwise it won't be crossed unless the signal goes all the way down to ground level.". Indeed. And perhaps add a more general statement about comparing for equality (with fixed values) for such ADC output (noise, rate of change of the input signal, etc.)?Peter Mortensen– Peter Mortensen2021年08月06日 21:11:43 +00:00Commented Aug 6, 2021 at 21:11
-
@PeterMortensen: This is generally quite relevant. However, the reasons why one should normally not compare an ADC value for equality do not quite apply to the special case of comparing to zero.Edgar Bonet– Edgar Bonet2021年08月06日 22:27:19 +00:00Commented Aug 6, 2021 at 22:27
analogRead(A0)
... that will tell you when it exits the while loop