analogWrite(pin,255) outputs a lower voltage than digitalWrite(pin,HIGH)

pixelk

Well-known member
The title says it all.

digitalWrite(pin,HIGH) outputs 3,29V
analogWrite(pin,255) outputs 3,28V average

It is problematic because I use PWM to drive a common anode RGB (within a rotary encoder) and can't get it tottaly off (because of the 0,01V potential).

Pwm 255

Is it because analogWrite(255) puts out a 255/256 duty cycle signal ? That would give a voltage of 3.3 * 255 / 256. = 3.287 V. I could check with an oscilloscope later.

The title says it all.

digitalWrite(pin,HIGH) outputs 3,29V
analogWrite(pin,255) outputs 3,28V average

It is problematic because I use PWM to drive a common anode RGB (within a rotary encoder) and can't get it tottaly off (because of the 0,01V potential).

OK, I checked. 255 PWM doesn't generate an output that is 100 % on.

The 3.3 V pin is at 3.3030 V
analogWrite(12, 255) generates a 1.46 kHz PWM with 255/256 duty cycle at 3.289 V (i.e. pulse is low for about 2.7 us every 684 us). On the other hand, digitalWrite(12, HIGH) measures 3.302 V (the 3.3 V supply varies somewhat). Similarily, analogWrite(12, 128) generates very close to 50 % duty cycle, measuring 1.6512 V (i.e. half of 3.303).

So the expression for the PWM is VOUT = 3.3*PWM/256, where PWM can be between 0 and 255. in order to keep an 8-bit PWM value, and to go monotonically from 0 to 100 %, the PWM needs to use 255 (not 256) as a divisor).

Thanks for your testing, I haven't anything I could call an oscilloscope and did my peasurment with my multimeter.

I just dove into the beta6 source code and I think I found the culprit

pins_teensy.c has void analogWrite(uint8_t pin, int val) on line 492 wich starts like this :
Code:
``````void analogWrite(uint8_t pin, int val)
{
uint32_t cval;

pinMode(pin, OUTPUT);
if (val <= 0) {
digitalWrite(pin, LOW);
pinMode(pin, OUTPUT);	// TODO: implement OUTPUT_LOW
return;
} else if (val > 255) {
digitalWrite(pin, HIGH);
pinMode(pin, OUTPUT);	// TODO: implement OUTPUT_HIGH
return;
}``````

The arduino counter parts sits in wiring_analog.c line 100 as void analogWrite(uint8_t pin, int val) :
Code:
``````void analogWrite(uint8_t pin, int val)
{
// We need to make sure the PWM output is enabled for those pins
// that support it, as we turn it off when digitally reading or
// writing with them.  Also, make sure the pin is in output mode
// for consistenty with Wiring, which doesn't require a pinMode
// call for the analog output pins.
pinMode(pin, OUTPUT);
if (val == 0)
{
digitalWrite(pin, LOW);
}
else if (val == 255)
{
digitalWrite(pin, HIGH);
}``````

There's a clear difference here. To be ardnuino's equivalent
} else if (val > 255) {
should at least be replaced by
} else if (val >= 255) {

Then there is the problem I talked about here, but I'm not sure it applies in this case.

Yeah, I think it's a case of wanting the PWM to be a fraction of 255 or of 256. With 255 counts, to go from 0 % to 100 % the argument would range from 0 to 255 -- an 8-bit number. With 256 counts (which might seem more 'natural' so some people), the argument needs to range from 0 to 256 -- not an 8-but number.

'val' is an int, so 8-bit isn't required.

The KL20 has a 12-bit DAC on board -- ultimately this would be a better analog output generator, but I don't know (yet) what pins it can be routed to.

Thanks for your testing, I haven't anything I could call an oscilloscope and did my peasurment with my multimeter.

I just dove into the beta6 source code and I think I found the culprit

pins_teensy.c has void analogWrite(uint8_t pin, int val) on line 492 wich starts like this :
Code:
``````void analogWrite(uint8_t pin, int val)
{
uint32_t cval;

pinMode(pin, OUTPUT);
if (val <= 0) {
digitalWrite(pin, LOW);
pinMode(pin, OUTPUT);	// TODO: implement OUTPUT_LOW
return;
} else if (val > 255) {
digitalWrite(pin, HIGH);
pinMode(pin, OUTPUT);	// TODO: implement OUTPUT_HIGH
return;
}``````

The arduino counter parts sits in wiring_analog.c line 100 as void analogWrite(uint8_t pin, int val) :
Code:
``````void analogWrite(uint8_t pin, int val)
{
// We need to make sure the PWM output is enabled for those pins
// that support it, as we turn it off when digitally reading or
// writing with them.  Also, make sure the pin is in output mode
// for consistenty with Wiring, which doesn't require a pinMode
// call for the analog output pins.
pinMode(pin, OUTPUT);
if (val == 0)
{
digitalWrite(pin, LOW);
}
else if (val == 255)
{
digitalWrite(pin, HIGH);
}``````

There's a clear difference here. To be ardnuino's equivalent
} else if (val > 255) {
should at least be replaced by
} else if (val >= 255) {

Then there is the problem I talked about here, but I'm not sure it applies in this case.

The priority would be (IMHO) to work more like the original Arduino code (to port existing projects easily).
Paul could then add a analogWriteEx or analogWriteRes which takes full benefit of the K20 features.
As why the original Arduino takes a int rather than a byte as value input, I have no idea. Maybe to favor ease of use (you don't need to convert your ints) over speed ? I didn't read further once I stumbled upon this bit of code.