1

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;
}
3
  • print the value of analogRead(A0) ... that will tell you when it exits the while loop
    – jsotola
    Aug 6 '21 at 6:27
  • Can you fix the indentation of the code? Aug 6 '21 at 20:48
  • Re "A sine wave is being fed to A0": What is its amplitude and DC offset? What is the output impedance of the signal generator (what kind of signal generator is it)? How is it coupled? AC coupled? Aug 6 '21 at 21:08
3

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.

3
  • 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
    Aug 6 '21 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. Aug 6 '21 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? Aug 6 '21 at 20:58
4

What you have implemented, perhaps unknowingly, is called hysteresis. Your program spends most of its time in either of these two states:

  1. 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.
  2. It is continually running the inner while loop, and the value read is > 0. It gets out of this state only when analogRead() 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 LOWHIGH 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;
    }
}
2
  • 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.)? Aug 6 '21 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. Aug 6 '21 at 22:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.