@PWM will never get to 100%

Good morning,
I am trying to use the following sketch in order to use any digital output as a PWM output; even if it has no PWM option.
The problem I have is that it works fine except it will never go to 100% with the pot value to Vcc 5 V.
I tried changing parameters and there is always the same value left, The Output stops at some 93% instead of 100% of Vcc.
I can't understand why, could anyone have an idea ? the problem happens on any board: Uno, Nano and even AtTiny.
The whole device behaves as if there was a resistor of about 2,5 K in series with the Pot input: I tried using a 10 K value pot and the max output I get is around 60% of max. With a 50 K value pot, max output is around 80%.

Thanks in advance.

There is no sketch and no schematics of your setup. Don't you also think this is necessary to have a chance to help you?

Sorry ! here is the code:

int frequency = 300; //the frequency of the cycle

int cycle_length; //how long each pwm cycle should be on for

float duty_cycle; //the percentage of power we want

int time_on; // how long the output should be high

int time_off; //how long the output should be low

float v_out; //the amount of voltage the led should draw

int pot_pin = A2; //pin with potentiometer

int pwm_pin = 3; // pin that will act like a pwm pin



void setup() {


pinMode(pot_pin, INPUT);

pinMode(pwm_pin, OUTPUT);

digitalWrite(pwm_pin, LOW);

//calculate the length of the cycle

cycle_length = 1000000/frequency; //length of one pwm cycle in microseconds

}



void loop() {



int val = analogRead(pot_pin); //get the pwm value

v_out= map(val, 0, 1023, 0, 255); //gets the pot value from 0-1024 and turns it into a value from 0-255

duty_cycle = v_out/255; // percentage of power being drawn as decimal.

time_on = duty_cycle * cycle_length; // work out the time it should be on

time_off = cycle_length-time_on; // work out the time it should be off



  if(time_on > 0)

  {

digitalWrite(pwm_pin, HIGH);

delayMicroseconds(time_on); // turns led on for short anount of time

  }

digitalWrite(pwm_pin, LOW);

delayMicroseconds(time_off);

}

Why don't you add some simple prints, which will show you where your arithmetic is going wrong?

Don't mix float and int. If you are not extremely careful with this the result will not be what you intended. Try to eliminate the floats as there the risk of rounding errors is quite high. If you stay with integers the error is at least faster visible.

You need to suppress setting the output low if the time off is zero?

  if (time_off > 0)
  {
    digitalWrite(pwm_pin, LOW);
    delayMicroseconds(time_off);
  }

Also change that 1000000/frequency to 1000000L/frequency since the value
is too big to be an int.

You don't need the L there. The compiler is smart enough to see that 1000000 is too big for an int and will promote it automatically.

Since the digitalWrite() takes about 6 microseconds you can get a little closer by adjusting the times:

const int frequency = 300; //the frequency of the cycle
const int cycle_length = 1000000L / frequency; //how long each pwm cycle should be on for
const int pot_pin = A2; //pin with potentiometer
const int pwm_pin = 3; // pin that will act like a pwm pin

void setup() {
  pinMode(pwm_pin, OUTPUT);
}

void loop() {
  float duty_cycle = analogRead(pot_pin) / 1023.0; // percentage of power being drawn as decimal.
  int time_on = duty_cycle * cycle_length; // work out the time it should be on
  int time_off = cycle_length - time_on; // work out the time it should be off

  if (time_on > 6) {
    digitalWrite(pwm_pin, HIGH);
    delayMicroseconds(time_on-6); // turns led on for short anount of time
  }

  if (time_off > 6) {
    digitalWrite(pwm_pin, LOW);
    delayMicroseconds(time_off-6);
  }
}

Good evening

Thanks to all, I will work along those lines.
I tried using only ints and no floats but the transition between 0 and 100% is then very abrupt.

It could come from those 6 usec you mention, I will try.

Good week end

Guillaume

v_out= map(val, 0, 1023, 0, 255); I tried {sigh}

Good evening Awol, an you please explain your "I tried"? I can't get your point ...

thanks !

Guillaume60:
Good evening Awol, an you please explain your "I tried"? I can't get your point ...

thanks !

Did you re-read his other post? It asked you to do something. Why did you ignore him?

Good morning
Sorry, I am not familiar with all those possibilities. Now is he talking about Serial prints or other kinds of prints ?
I will add some Serial prints in the sketch meanwhile.

Have a good day.

Well, problem is now solved.
The frequency has to a multiple of 3, if not there is a rounding error which happens and the output never goes 100% high.

Thanks to all, Pylon for the rounding error clue and Awol for the suggestion of adding some Serial.prnts.

here is the sketch and it does go up to 100%:

loat frequency = 333; //the frequency of the cycle

float cycle_length; //how long each pwm cycle should be on for

float duty_cycle; //the percentage of power we want

int time_on; // how long the output should be high

int time_off; //how long the output should be low

float v_out; //the amount of voltage the led should draw

int pot_pin = A0; //pin with potentiometer

int pwm_pin = 2; // pin that will act like a pwm pin



void setup() {

Serial.begin(19200);

pinMode(pot_pin, INPUT);

pinMode(pwm_pin, OUTPUT);

digitalWrite(pwm_pin, LOW);

//calculate the length of the cycle

cycle_length = 1000000/frequency; //length of one pwm cycle in microseconds

}



void loop() {

int val = analogRead(pot_pin); //get the pwm value

v_out= map(val, 0, 1023, 0, 255); //gets the pot value from 0-1023 and turns it into a value from 0-255
Serial.print("vout ");
Serial.println(v_out);
delay(2000);

duty_cycle = v_out/255; // percentage of power being drawn as decimal.
Serial.print("dutycycle ");
Serial.println(duty_cycle);
Serial.println();

time_on = duty_cycle * cycle_length; // work out the time it should be on
Serial.print("timeon  ");
Serial.println(time_on);

time_off = cycle_length-time_on; // work out the time it should be off
Serial.print("timeoff  ");
Serial.println(time_off);
Serial.println();

delay(1000);

if(time_on > 0)

  {

digitalWrite(pwm_pin, HIGH);
Serial.print("pwmpin is HIGH ");
Serial.println();

delayMicroseconds(time_on); // turns Output on for the said amount of time

  }

digitalWrite(pwm_pin, LOW);


delayMicroseconds(time_off);

}

It is probably not perfect but it works.

Have a good day !

v_out= map(val, 0, 1023, 0, 255);Did you look at the output of that?

v_out = val / 4;is a lot simpler, (though not equivalent) but is probably what you want.

Good evening Awol
I will try it ! seems very clever indeed, if I want to use i on an Attiny it will be useful indeed, since there is little room in the memory.
Thanks !

Awol, I just tried it but the output does not go to 100%.
In fact the sketch does show it goes to 100% on all Serial prints, but the physical output does not !

So it works theoretically but not in practical uses !
Your solution has the same behavior.
Hardware issue ?
I tried it on all sorts of Arduino platforms, the same.

Maybe the input doesn't go to 100%

Just a thought.

Guillaume60:
In fact the sketch does show it goes to 100% on all Serial prints, but the physical output does not !

Of course it does not. At the bottom of loop() you say:

digitalWrite(pwm_pin, LOW);
delayMicroseconds(time_off);

You are telling it to turn off the output pin until you turn it on again. You can't have the output stay on 100% when you turn it off. What happened to the "if (time_off > 0)"?!?

Dear John,
The time_off duration is only after an 'if' test. It is a difference between total time and time_on. If the output is positive and HIGH at 100% then no time_off will happen. In the case of 100% the time_off duration is bypassed.

Awol, the input does go to 100%, the Prints show it.

Have a good day.