PDA

View Full Version : analogWrite(pin,255) outputs a lower voltage than digitalWrite(pin,HIGH)



pixelk
10-25-2012, 07:07 PM
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).

Jp3141
11-05-2012, 01:27 AM
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).

Jp3141
11-05-2012, 04:05 AM
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).

pixelk
11-05-2012, 12:31 PM
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 :

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) :

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 (http://forum.pjrc.com/threads/9-digitalWrite-doesn-t-work-after-analogWrite-on-the-same-pin), but I'm not sure it applies in this case.

Jp3141
11-05-2012, 08:16 PM
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 :

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) :

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 (http://forum.pjrc.com/threads/9-digitalWrite-doesn-t-work-after-analogWrite-on-the-same-pin), but I'm not sure it applies in this case.

pixelk
11-05-2012, 08:49 PM
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.