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 Answer 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.
-
1arduino 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– dannyf2017年03月31日 12:31:32 +00:00Commented Mar 31, 2017 at 12:31
-
Yes @dannyf that's certainly more reliable. In my case the relevant bit is
COM4D1
. Edited answer to match.101– 1012017年04月01日 03:40:45 +00:00Commented Apr 1, 2017 at 3:40 -
If your own answer works please accept it.2017年08月04日 07:35:08 +00:00Commented Aug 4, 2017 at 7:35
digitalRead(pin)
will tell you if it's 0 or 1if (timer != NOT_ON_TIMER) turnOffPWM(timer);
. I want to read the value without changing it @dandavis.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.analogWrite2(pin, val)
, which sets an external array of values by the pin number, then calls the realanalogWrite()
with the passed values. You can refer to the array from anywhere for a fast reliable read. Just run a replaceAll w/analogWrite2
...