1

I'm outputting an analog voltage by setting an 8-bit duty cycle using analogWrite. Once set, I'd like a way to read back this duty cycle. I've managed to read the duty cycle back from the OCR4D register, which matches the value set, e.g.:

pinMode(6, OUTPUT);
analogWrite(6, 123);
Serial.print(OCR4D);   // 123

However, when setting the duty cycle to 0 or 255, OCR4D is not actually set at all; instead analogWrite calls digitalWrite behind the scenes and the pin is digitally set to LOW or HIGH. This is seen in the source for analogWrite:

if (val == 0)
{
    digitalWrite(pin, LOW);
}
else if (val == 255)
{
    digitalWrite(pin, HIGH);
}
else
{
// set PWM duty cycle as normal...

The problem with this is that OCR4D is simply left at its previous value, so it's not always possible to tell the actual output by reading OCR4D alone. In other words there is no way to tell the difference between: - PWM is active and OCR4D reflects the duty cycle - PWM is disabled and OCR4D reflects a previous duty cycle (which is not useful)

Which register(s) can I read to crosscheck the PWM is active?

I'm also aware of the pulseIn function, but would rather read the PWM register directly.

  • 1
    digitalRead(pin) will tell you if it's 0 or 1 – dandavis Mar 30 '17 at 6:00
  • Unfortunately it also turns off PWM for that pin: if (timer != NOT_ON_TIMER) turnOffPWM(timer);. I want to read the value without changing it @dandavis. – 101 Mar 30 '17 at 23:19
  • i thought you said that PWM was already turned off for those values? you would only use the digitalRead if the other failed. – dandavis Mar 30 '17 at 23:27
  • Ah, sorry @dandavis my question wasn't very clear, I've since updated it. PWM does get disabled for those values, but it's not possible to tell if OCR4D is "wrong" as it always returns a valid duty cycle (it just keeps the old value). What I'm after is a way to tell if the PWM is active and that value is being applied. – 101 Mar 31 '17 at 3:32
  • ahh. the way i do this in my projects is to define analogWrite2(pin, val), which sets an external array of values by the pin number, then calls the real analogWrite() with the passed values. You can refer to the array from anywhere for a fast reliable read. Just run a replaceAll w/ analogWrite2... – dandavis Mar 31 '17 at 3:35
1

In the end I managed to do this by reading the timer/counter control register relevant to pin 6:

// read the voltage on an analog output pin
int analogReadOutput(int pin) {
    switch(digitalPinToTimer(pin)) {
        case TIMER4D:
            if (TCCR4C & (1 << COM4D1)) {   // PWM active
                return OCR4D;
            } else {                        // PWM not active
                return 255 * digitalRead(pin);
            }
        case TIMER4A:
            if (TCCR4A & (1 << COM4A1)) {   // PWM active
                return OCR4A;
            } else {                        // PWM not active
                return 255 * digitalRead(pin);
            }
        default:
            return -1;
    }
}

Note that this assumes two things that possibly aren't always true (outside of my simple program):

  • COM4D1 bit is a reliable way of telling if PWM is enabled,
  • duty cycle value is 8 bit with a max of 255.
  • 1
    arduino turns on/off the COMx bits for pwm controls -> take a look at turnOffPWM(). So a more reliable way is to read those bits to determine if a pin in in pwm or digital mode. actually a simple modification of turnOffPWM() would be more consistent in my view. – dannyf Mar 31 '17 at 12:31
  • Yes @dannyf that's certainly more reliable. In my case the relevant bit is COM4D1. Edited answer to match. – 101 Apr 1 '17 at 3:40
  • If your own answer works please accept it. – Nick Gammon Aug 4 '17 at 7:35

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.