Using digitalRead() for checking when PWM is HIGH or LOW

I'm having trouble with my code to read the state of a PWM pin.

I think there are three separate problems:

  1. The code in the command braces of an "if" conditional (which is itself in a "while" cycle) seems to be being run even though the conditional is not met.
  2. It appears that not all of the code in the command braces is being run properly.
  3. I can not seem to be able to read when the PWM switches between HIGH and LOW. It seems to always read either HIGH or always LOW.

(This problem is related to a question I asked in the sensor forum http://arduino.cc/forum/index.php/topic,145724.0.html).

I reduced the code down to a test problem, based on the LED fade default sketch example.
An LED is connected between digital pin 11 and ground.
In the setup the digital pin 11 is set to PWM with a value of 70.
In the loop there are two separate cycles:
The first cycle repeats until digitalRead() reads the state of the PWM pin to be HIGH, and then makes the LED bright and prints the word "high" over serial.
The second cycle repeats until digitalRead() reads the state of the PWM pin to be LOW, and then makes the LED dim and prints the word "low" over serial.
There is a 2 second delay between each cycle, in order to see the change.

It should start with a brightly lit LED, then print "high", then dim the LED and print "low", then brighten the LED and print "high" - and so on.
What actually happens is that the LED dims and brightens every 2 seconds, but only the word "low" ever gets printed across the serial.

This is odd because once the LED is dimmed, it should not be brightened again unless the conditional inside the first cycle is met - which requires digitalRead() to have read HIGH. If that conditional is met, then the word "high" should also be printed across the serial, which is it not being. Hence suggestion 2) that not all the code in the command braces is being run properly.

Also, if I remove the element in the first while cycle condition which limits the number of times that the cycle runs to 10, then it seems to just get stuck in an infinite loop. This infinite loop can only happen if the variable high_reading_taken remains set to 0, and that can only happen if the conditional which checks whether PWM is set to HIGH is never met. This either means that digitalRead() is never reading the PWM pin as HIGH, or that the code in the command braces is not being run properly. This really does not make sense, because when the loop is only allowed to run 10 times before failing then the LED cycles through dim and bright, which should only happen if the conditional inside the first cycle is met - but when the loop is not limited to 10 then the LED change never gets triggered, which it SHOULD do if the conditional inside the cycle is met as suggested by the results seen when there is no limit. Simply adding a conditional to the while cycle to allow it only to run 10 times before moving on should not affect the digitalRead() process. This all MUST therefore mean that in the scenario where the number of run throughs of the first cycle is limited to 10, then the code inside the command braces is being triggered EVEN THOUGH THE CONDITIONAL WHICH USES DIGITALREAD() TO GET THE PWM VALUE IS NOT BEING MET! Doesn't it??

Have I missed something really obvious here? Either there is a serious problem with the processing of the code, or I have something badly misunderstood. I presume the problem is me, but I can not see where I have gone wrong :frowning:

The code I used:

int baud = 9600;
int PWMPin= 11;           // the pin that the LED is attached to
int read_attempts_max = 10;
int high_reading_taken;
int low_reading_taken;
int read_attempt;

void setup()  { 
  
  Serial.begin(baud);  
  pinMode(PWMPin, OUTPUT);
  analogWrite(PWMPin, 70);  
  
} 

void loop()  { 
              
        // HIGH cycle
        high_reading_taken = 0;
        read_attempt = 1;
        while ( (high_reading_taken == 0) && (read_attempt <= read_attempts_max) )
              {
              if ( digitalRead(PWMPin) == HIGH ) 
                 { 
                 high_reading_taken = 1; 
                 analogWrite(PWMPin, 70); 
                 Serial.println("high"); 
                 }           
              read_attempt++;
              }
            
        delay(2000);    
                      
        // LOW cycle                          
        low_reading_taken = 0;
        while ( (low_reading_taken == 0) )
              {
              if ( digitalRead(PWMPin) == LOW ) 
                 { 
                 low_reading_taken = 1; 
                 analogWrite(PWMPin, 1); 
                 Serial.println("low"); 
                 }              
              }   
            
        delay(2000);      
        
}

If you just want to poll the input state and wait for it to go high and low, why don't you just do that and get rid off all these flags and counters?

// wait for the input to go HIGH
while(digitalRead(PWMPin) != HIGH)
{
    // do nothing
}
Serial.println("HIGH");

// wait for the input to go LOW
while(digitalRead(PWMPin) != LOW)
{
    // do nothing
}
Serial.println("LOW");

A PWM pin toggles between HIGH and LOW faster than digitalRead() can react. Even if it didn't, what do you think you will learn from knowing whether the pin is HIGH or LOW at the moment you read it?

PaulS:
A PWM pin toggles between HIGH and LOW faster than digitalRead() can react. Even if it didn't, what do you think you will learn from knowing whether the pin is HIGH or LOW at the moment you read it?

Paul, a PWM pin toggles at about 490Hz unless you change the PWM frequency, which means you can read its state many times in a single cycle.

The reason that the OP wants to read the state (as described in a thread elsewhere on the forum) is that he is using the PWM pin to generate a square wave, and he wants to measure the amplitude of a square wave derived from it. So he wants to synchronize analogRead calls to the state changes.

Peter, I'm seeing the same problem. Reading the datasheet, it looks like the data in buffers are disabled when certain pins are used for alternate functions such as PWM. I can think of a few ways round this:

  1. If you have a spare pin, connect it to the PWM pin and digitalRead the value from that pin.

  2. Configure the timer/counter that generates the PWM signal to generate an interrupt on the state change, and synchronize the analogRead calls to that interrupt.

  3. Don't bother synchronizing the analogRead calls, just take a number of readings (say over 100ms), and take the difference between the highest and lowest as the signal amplitude.

dc42:
Peter, I'm seeing the same problem. Reading the datasheet, it looks like the data in buffers are disabled when certain pins are used for alternate functions such as PWM.

I don't quite follow what you mean by that. If you mean that digitalRead() (of some pins) is not available (returns false values etc) depending on what is being done with other pins then that's the first suggestion I've ever seen of that.

OK, thanks. I tried suggestion 3.

  1. Don't bother synchronizing the analogRead calls, just take a number of readings (say over 100ms), and take the difference between the highest and lowest as the signal amplitude.

New code:

        sensor_high = -1;
        sensor_low = 2000;     
        total_readings = 0;      
        while ( total_readings <= MaxTotalReadings )
              {
              value = analogRead(0);
              if ( value > sensor_high) { sensor_high = value; }
              if ( value < sensor_low) { sensor_low = value; }
              total_readings++;  
              }

Taking 1000 readings (either with or without a delay of 1ms between each reading) consistently gives a highest reading of 183 and a lowest reading of 180 when connected to a potato (powered from USB), 184 and 179 when connected to a wooden table (powered from USB), and 182 and 180 when connected to a human hand (powered from 9v battery). I'm guessing that is about right, because a hand is likely to give the least resistance. I'll try to improve the sensitivity with better resistors, ceramic capacitor and maybe analogReference().

Thanks a lot for you help dc42!

A USB powered potato. What will they think of next ?

kayle:
Taking 1000 readings (either with or without a delay of 1ms between each reading) consistently gives a highest reading of 183 and a lowest reading of 180 when connected to a potato (powered from USB), 184 and 179 when connected to a wooden table (powered from USB), and 182 and 180 when connected to a human hand (powered from 9v battery). I'm guessing that is about right, because a hand is likely to give the least resistance. I'll try to improve the sensitivity with better resistors, ceramic capacitor and maybe analogReference().

It sounds to me that you need to increase the values of both resistors by a factor of 10 or more. That in turn means that you can use a lower value ceramic capacitor. Also call analogReference to select the internal bandgap reference.

Taking 1000 readings (either with or without a delay of 1ms between each reading) consistently gives a highest reading of 183 and a lowest reading of 180 when connected to a potato (powered from USB) ...

Well, that made my day.

If you read it, and then read it again, and then read it again, it will always read, either HIGH or LOW. How would you expect anything else ?

If it's subject to PWM output, it might change.

Taking 1000 readings (either with or without a delay of 1ms between each reading) consistently gives a highest reading of 183 and a lowest reading of 180 when connected to a potato (powered from USB), 184 and 179 when connected to a wooden table (powered from USB), and 182 and 180 when connected to a human hand (powered from 9v battery). I'm guessing that is about right, because a hand is likely to give the least resistance. I'll try to improve the sensitivity with better resistors, ceramic capacitor and maybe analogReference().

From reading this, I get the feeling that you and I are talking about two different things. What a PWM pins is powering is completely irrelevant to the value that you would read from the pin, UNLESS what you are powering is drawing too much current.

The analogRead() function is not the same as the digitalRead() function mentioned in the title, has nothing to do with PWM, and is not reading the same physical pin that PWM is being performed on.

So what the hell are you trying to do?

I don't understand how you power a potato, a wooden table (or how a wooden table is powered by USB), or a human hand (or how a human hand is powered by a (useless) 9V battery).

We need pictures.

The analogReference() function has nothing to do with digitalRead() or analogWrite(), so you are barking up the wrong tree there.

The code in the command braces of an "if" conditional (which is itself in a "while" cycle) seems to be being run even though the conditional is not met.

Code inside an "if" will not be run if the condition is not met. Take that as a fact.

potato

What more needs to be said?

It appears that not all of the code in the command braces is being run properly.

If you can define "run properly" as opposed to "run improperly" we might get somewhere.

Thanks everyone. This problem is solved as of Reply # 5 (and improved by Reply # 7, thanks again dc42; I will aim for that).

I seem to have caused a bit of confusion with my sentence structure. It was always the arduino which was either powered by USB or by a battery. I don't have a USB powered potato, unfortunately. I wish I had now though. I bet those things would sell like hot cakes. Maybe I should patent the idea ...

Responses:

If you read it, and then read it again, and then read it again, it will always read, either HIGH or LOW. How would you expect anything else ?

That's kind of the point. The idea is to take a reading from an analog pin when a digital PWM pin is HIGH, and then again when it is LOW - without using digitalRead() to get the state of the PWM pin. Maybe 1000 readings was a bit of overkill though.

The analogReference() function has nothing to do with digitalRead() or analogWrite(), so you are barking up the wrong tree there.

The analogReference() is for fine tuning the analog pin which is doing the reading using analogRead() of a value which depends upon the output of a digital pin set to PWM with analogWrite(), at a time specified by the result of digitalRead() of that PWM pin. This is related to the problem which spawned this question, that I referenced in my first post. The code I posted above in my first post in this thread refers only to a separate simplified example using an LED to show the problem I am having with reading the state of a PWM pin. Sorry for the confusion!

Code inside an "if" will not be run if the condition is not met. Take that as a fact.

Exactly. I assumed that the command braces should not run if the conditional is not met, so either my code is incorrect, or there is some kind of problem with the electronics - hence my question here.

If you just want to poll the input state and wait for it to go high and low, why don't you just do that and get rid off all these flags and counters?

I used flags because I was originally printing the value of the flag over the serial to check the outcome, and they just remained as a replic when I switched to printing words instead.
Admittedly using != HIGH or != LOW is more elegant, thanks.
I used the counter to limit the run throughs of the first cycle, after I realised it was getting stuck in an infinite loop.

If you can define "run properly" as opposed to "run improperly" we might get somewhere.

When the first cycle is limited to only 10 run throughs, then the loop() function runs fine and the LED fades up and down. The LED can only change brightness if the code in the command braces of the two conditionals (one in each cycle) is run - specifically if the analogWrite() code is run. I assume this is correct, since the only other place I use digitalWrite() is in the setup() function. However, when this occurs then the LED blinks, but no value is printed over the serial in the command braces which runs when the PWM pin is detected as HIGH. There is a value printed when the PWM pin is detected as LOW. Therefore the analogWrite() command in each set of braces is being run correctly in both conditionals, but the Serial.println() command is not. Either this is a problem with the Serial.println() or with the running of the command braces. It seems unlikely to be a problem with Serial.println(), because that exact command is working fine in the first cycle, and it is printing a pre-defined value, not the actual state of the PWM pin. Hence my suggestion that the problem is with the running of the command braces.

Furthermore, the analogWrite() command to brighten the LED can only occur if the conditional it is sitting in correctly uses digitalRead() and gets the state of the pin to be HIGH, and since the LED is blinking then that suggests the read is being performed properly. This changes when the limit of 10 passes is removed from the first while condition (by deleting the second condition in the brackets). When there is no limit, then the whole thing hangs seemingly in an infinite loop, and the LED does not change it state, and nothing is printed over the serial. This can only occur if the digitalRead() of the PWM is now never reading the state as HIGH - even though it was apparently able to read it as HIGH when there was no limit on the number of times the cycle could run. This can only happen if digitalRead() is suddenly not able to read when the PWM is HIGH, or if the condition is being met but the commands associated with it are not being run.

As far as I can see, the results are not what would be expected. I was not able to find any other info on a problem like this. I wondered whether there was a known issue with the board in this type of instance, which would cause this apparently unusual behavior in the way the code is being run, or whether in fact the fault is with me and I have made a school boy error with my code.

But either way, whether my code is wrong or there is a problem with the board, I no longer require the answer to the original question because I have switched to using a loop that just runs analogRead() and skips the implementation of the digitalRead(). It was digitalRead() which was causing the problem.

Thanks all.

kayle:
I don't have a USB powered potato, unfortunately. I wish I had now though. I bet those things would sell like hot cakes. Maybe I should patent the idea ...

Give it a bluetooth interface and you could be on to a real winner. :slight_smile:

This can only happen if digitalRead() is suddenly not able to read when the PWM is HIGH, or if the condition is being met but the commands associated with it are not being run.

PWM output on a pin is done by setting a bit on the timer which (from the datasheet):

... overrides the normal port functionality of the I/O pin it is connected to ...

Your investigations would appear to show that you are reading back the last value set (by you), not what the PWM output is setting it to.

Good morning

Arduino IDE 1.0.1

This week I've found this kind of problem...
I've build my own IO table on my LCD to show Input and Output states...
I've found that my Pin 11 and 12 (PWM ports), defined as Output, when I use 'digitalRead(pin)' to check if I apply 'x' or ' ' on LCD, the leds on my breadboard will blink! On... Off...On...Off...

It seems that exist some interrupt...

How can I avoid situation?

Thanks on advance
Pedro Ferrer