4

I am using Arduino UNO/MEGA, I want to

analogWrite(pin, val); 

where the value is 0-1023.

How can I make that ?

Thanks.

  • 1
    It seems there are two types of answers emerging - those that increase the resolution of the PWM, and those that adapt your data to fit the existing resolution, using something like map. I find both types of answers to be useful, but you might want to clarify what your intention is for going to 1023. – GuitarPicker Nov 21 '16 at 19:08
  • @GuitarPicker, the data that returns from analogRead is from 0-1023 – Dat Ha Nov 21 '16 at 19:44
  • @GuitarPicker The data comes from analogRead is 0-1023, how can I output the same data using pwm, that's is . – Mostafa Khattab Nov 22 '16 at 6:51
11

Section 16, "16-bit Timer/Counter1 with PWM" of Atmel-8271J-AVR- ATmega-Datasheet_11/2015 describes using Timer/Counter-1 for 16-bit PWM, including sample code in both C and assembler. It might take a couple of reads over that section to get familiar with the hardware and learn what else T/C-1 is used for, and that you might have to give up while using it for PWM, but the code is fairly straightforward.

Using this T/C would give you 2-bits more resolution in your PWM output than mapping onto an 8-bit counter, if that is what your project needs.

+1 for the question. Not having needed PWM output for anything more than a demo with my DMM (yet), it hadn't occurred to me to look for a higher resolution until now.

  • This is the correct answer. Neither of the other 2 answers say how to output the resolution he wants. – v7d8dpo4 Nov 22 '16 at 14:01
9

The simplest way is to change your value range to 0-255. You can't change analogWrite's range, since that is fixed in the core software.

analogWrite(5, val >> 2);

The >> 2 bit-shifts the value two bits to the right, turning a 10-bit value (0-1023) into an 8-bit value (0-255). It's by far the most efficient way of dividing by 4.

On more complex 32-bit boards, like the Arduino Due, or the chipKIT range of boards, etc, you have the function analogWriteResolution() which can change the range, but that cannot be done on the 8-bit boards.

From what I have read online it looks like the ATMega chips may support higher resolutions for PWM - however there is no support in the core for that. If you need a higher resolution for some specific reason then you will need to program the registers manually yourself and not rely on the Arduino API to do the work for you. For that you will need to consult the datasheet for your chosen chip.

  • I'm pretty sure the compiler will optimize x / some_constant_power_of_two to x >> exponent_of_the_number. – Kroltan Nov 21 '16 at 21:05
  • @Kroltan Quite probably. It's something I would hope it did do. Personally I like to be explicit though, just on the offchance that it doesn't. – Majenko Nov 21 '16 at 21:21
5

I agree with @Majenko's answer, though here is a more "beginner" friendly method of doing the same.

val = map(val,0,1023,0,255);
analogWrite(pin, val);

Not the most efficient, but still...

2

The other questions so far have correctly addressed one possible meaning of this question, "how can I increase the resolution?" Here's another possible interpretation.

How can I increase the output magnitude?

It sounds like you might want to output a higher maximum voltage than what you get from using analogWrite(pin,255) on the Uno or Mega. This is not possible using the Arduino by itself, because PWM represents a fraction of the maximum output, not an absolute value.

On the Uno and Mega, that maximum output is 5V, so the approximate values come out to:

analogWrite(pin,0);   // (0 / 255) * 5V   (0% of 5V == 0V)
analogWrite(pin,127); // (127 / 255) * 5V (50% of 5V == 2.5V)
analogWrite(pin,255); // (255 / 255) * 5V (100% of 5V == 5V)

If you want to make a higher absolute voltage, you will need an external power source and a method of translating the 0-5V output into another voltage range. This is called "level shifting" and there are standalone ICs which can do this job. Motor drivers and H-Bridge devices utilize this principle as well.

If you use some sort of level shifting circuitry, then you can map the analogWrite to a different range, such as 0-20V:

analogWrite(pin,0);   // (0 / 255) * 20V   (0V)
analogWrite(pin,127); // (127 / 255) * 20V (10V)
analogWrite(pin,255); // (255 / 255) * 20V (20V)

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.